mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
Full proof read and edit
This commit is contained in:
@@ -19,16 +19,20 @@ NOTE: Other public classes are not subject to
|
||||
the following rules; they may be changed in any way, or even removed.
|
||||
|
||||
- Class and method names, types, and number of parameters in already existing methods, should not be changed.
|
||||
|
||||
- Any methods can be added to the classes and interfaces that are not subject for
|
||||
subclassing or implementing by the library users; such as `ChronicleMap`, `ChronicleMapBuilder`,
|
||||
`MapEntry`. They must be added in such a way that any existing code calling methods on those classes still compile; and not
|
||||
`MapEntry`. They must be added in such a way that any existing code, calling methods on those classes still compiles; and does not
|
||||
break due to ambiguity in method resolution.
|
||||
|
||||
- Only non-abstract (concrete or default) methods can be added to the classes and interfaces that are subject for subclassing by the library users; such as `MapMethods`, `MapEntryOperations`, `SizedReader`.
|
||||
|
||||
=== Behaviour
|
||||
|
||||
Semantics and actual effects of API calls may change between Chronicle Map versions with
|
||||
different `<minor>` number versioning. For example, default serialization forms of key or value objects of some types could change, "high level" configurations in `ChronicleMapBuilder` could produce
|
||||
different `<minor>` number versioning.
|
||||
|
||||
For example, default serialization forms of key or value objects of some types could change, "high level" configurations in `ChronicleMapBuilder` could produce
|
||||
Chronicle Maps with different "low level" configurations, etc.
|
||||
|
||||
Such changes should be documented in the release notes.
|
||||
@@ -39,24 +43,25 @@ A persisted Chronicle Map store (a binary file), produced by a production releas
|
||||
|
||||
To support this:
|
||||
|
||||
- Serialization forms of any classes should not be changed. For example, when a marshaller class (say, `StringBytesReader`) of objects of some type is changed and its serialization form should be changed, a new class called `StringBytesReader_3_5` (5 is the `<minor>` number of the version of the library, in which this change is made) should be created, and put along with older class.
|
||||
- Serialization forms of any classes should not be changed. For example, when a marshaller class (for example, `StringBytesReader`) of objects of some type is changed, and its serialization form should be changed, a new class called `StringBytesReader_3_5` (`5` is the `<minor>` number of the version of the library, in which this change is made) should be created, and put along with older class.
|
||||
|
||||
- When the specification revision is updated (hence binary form could be completely changed), a new version of the root implementation class (say, `VanillaChronicleMap_3_5`) should be created and put along with older version, which should behave the same as before.
|
||||
- When the specification revision is updated (where the binary form could be completely changed), a new version of the root implementation class (say, `VanillaChronicleMap_3_5`) should be created and put along with older version, which should behave the same as before.
|
||||
|
||||
=== Replication Protocol
|
||||
|
||||
The replication protocol may change between Chronicle Map versions with different `<minor>` numbers.
|
||||
|
||||
Chronicle Map nodes should communicate with other nodes of the same version.
|
||||
Chronicle Map nodes should only communicate with other nodes of the same version.
|
||||
|
||||
Chronicle Map nodes running different versions of the library should not communicate to each other. This may lead to failures, but not to data corruption on either of the nodes.
|
||||
Chronicle Map nodes running different versions of the library should not communicate with each other. This may lead to failures, but not to data corruption on either of the nodes.
|
||||
|
||||
== Specification Compatibility
|
||||
|
||||
The Chronicle Map Data Store specification can be updated without changing the revision while it remains backward compatible; that is, the implementation of the newer version of the specification can access and update any persisted Chronicle Map store, produced by an older version.
|
||||
In addition, potential *concurrent* accessing implementations of older versions ("within" the same revision) should not lose correctness and corrupt the data.
|
||||
The Chronicle Map Data Store specification can be updated without changing the revision while it remains backward compatible. Backward compatibility means that the implementation of the newer version of the specification can access and update any persisted Chronicle Map store that was produced by an older version.
|
||||
|
||||
If more substantial changes to the specification are required, the revision number should be updated.
|
||||
In addition, potential *concurrent* accessing implementations of older versions ("within" the same revision) should not lose correctness or corrupt the data.
|
||||
|
||||
If more substantial changes to the specification are required, then the revision number should be updated.
|
||||
|
||||
The binary form of Chronicle Map and its algorithms could change between revisions to any degree, but link:../spec/1-design-goals.md#guarantees[guarantees] provided by the specification are not weakened.
|
||||
|
||||
@@ -74,7 +79,7 @@ For example:
|
||||
|
||||
- **a feature, or public API** method is added.
|
||||
|
||||
- **the behaviour** of `ChronicleMap`, `ChronicleMapBuilder`, or any other class or method or subsystem, is semantically changed.
|
||||
- **the behaviour** of `ChronicleMap`, `ChronicleMapBuilder`, or any other class or method or subsystem, is semantically changed. +
|
||||
For example, when configuration heuristics in `ChronicleMapBuilder` are improved, and
|
||||
`ChronicleMapBuilder` configured the same way constructs a `ChronicleMap` with a different number of segments, or segment's capacity.
|
||||
|
||||
@@ -87,13 +92,13 @@ For example:
|
||||
** the binary replication protocol, or the serialized form of some marshaller classes, that require creation of new versions of classes.
|
||||
|
||||
=== The <minor> number should not be updated when:
|
||||
- **a bug fixed**, in a way that doesn't make any working code to change its behaviour semantically.
|
||||
- **a bug is fixed**, in a way that does not make any working code to change its behaviour semantically.
|
||||
- **performance is optimized**, changing its performance characteristics,
|
||||
but not semantics.
|
||||
|
||||
During development, and after the release of the version `3.y.z`, the library should be versioned `3.y.<x+1>-SNAPSHOT`. +
|
||||
+
|
||||
As soon as changes made that are substantial enough for the next released version
|
||||
As soon as changes are made that are substantial enough for the next released version
|
||||
to update the `<minor>` number, then the version should be changed to `3.<y+1>.0-SNAPSHOT`, *in the same
|
||||
commit operation* that these changes are made.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ toc::[]
|
||||
|
||||
== License
|
||||
|
||||
Chronicle Map is distributed under LGPLv3. If you want to obtain this software under more permissive
|
||||
Chronicle Map is distributed under `LGPLv3`. If you want to obtain this software under a more permissive
|
||||
license, please contact mailto:sales@chronicle.software.
|
||||
|
||||
== Maven Artifact Download
|
||||
@@ -47,7 +47,7 @@ A better way, is to add the following to your `setting.xml` file to allow Maven
|
||||
</snapshots>
|
||||
</repository>
|
||||
```
|
||||
and define the snapshot version in your `pom.xml`; for example:
|
||||
You can define the snapshot version in your `pom.xml`; for example:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
|
||||
+7
-6
@@ -2,24 +2,25 @@
|
||||
|
||||
== Question
|
||||
|
||||
I am investigating Chronicle Map as a potential replacement of `Redis`; with concurrency in mind. In our architecture, we would be looking to replace a "large" `Redis` instance that currently has multiple clients connecting to it, causing latency pileups due to ``Redis``'s blocking nature.
|
||||
I am investigating Chronicle Map as a potential replacement of `Redis`; with concurrency in mind.
|
||||
|
||||
In our architecture, we would be looking to replace a "large" `Redis` instance that currently has multiple clients connecting to it, causing latency pileups due to ``Redis``'s blocking nature.
|
||||
|
||||
The issue is that we need to make requests in random batches of ~1000. With `Redis` we are able to make a single request via a `Lua` script (or multi-get / multi-set commands) and receive a single response. In the documentation on Chronicle Map`s stateless client, I see that the remote calls are blocking, and can be made only one key at a time; so for us the solution is not obvious.
|
||||
|
||||
While I am considering passing off each individual key task to a threadpool running X blocking threads at a time, I wonder if there might be a better solution, that could take advantage of doing RPC in batches, and perhaps work asynchronously. As I do not see this available currently, my question is whether this is an enhancement that you might consider, or if you could perhaps point me to if/how we could write our own solution for doing this.
|
||||
While I am considering passing off each individual key task to a threadpool running `x` blocking threads at a time, I wonder if there might be a better solution, that could take advantage of doing RPC in batches, and perhaps work asynchronously. As I do not see this available currently, my question is whether this is an enhancement that you might consider, or if you could perhaps point me to if/how we could write our own solution for doing this?
|
||||
|
||||
Also, is there a reason these `1000` `gets` have be done serially in one thread? Why not submit `1000 get()` tasks to a pool of say `20` threads. Wouldn't this improve throughput and reduce latency?
|
||||
|
||||
== Answer
|
||||
|
||||
The stateless client has been replaced with Engine. It is already not supported for
|
||||
ChronicleMap 3.x. For more information, please refer to Chronicle Engine FAQ's.
|
||||
The stateless client has been replaced with Chronicle Engine. The stateless client is not supported in ChronicleMap 3.x. For more information, please refer to Chronicle Engine FAQ's.
|
||||
|
||||
For `get()s`, parallelizing will reduce costs. For `put()s`, if you have concurrency requirements, that is, multi-key lock before updating all of them, it should be in one thread.
|
||||
|
||||
You would not gain a performance benefit by using batches, unless you are compressing the batch of data. All the data will have to be sent via TCP, even if it's in a batch.
|
||||
You would not gain a performance benefit by using batches, unless you are compressing the batch of data. All the data will have to be sent via TCP, even if it is in a batch.
|
||||
|
||||
NOTE: Under high load the Chronicle Map stateless client consolidates many small TCP requests into a single request when run with a number of threads.
|
||||
NOTE: Under high load, the Chronicle Map stateless client consolidates many small TCP requests into a single request when run with a number of threads.
|
||||
|
||||
|
||||
'''
|
||||
|
||||
+28
-24
@@ -9,11 +9,11 @@ Neil Clifford
|
||||
toc::[]
|
||||
|
||||
Chronicle Map is an in-memory, key-value store, designed for low-latency, and/or multi-process
|
||||
applications such as trading and financial market applications.
|
||||
applications, such as trading and financial market applications.
|
||||
|
||||
== Features
|
||||
- **Ultra low latency**: Chronicle Map targets median latency of both read and write queries of less
|
||||
than 1 *micro*second in https://github.com/OpenHFT/Chronicle-Map/search?l=java&q=perf&type=Code[certain tests].
|
||||
than 1 microsecond in https://github.com/OpenHFT/Chronicle-Map/search?l=java&q=perf&type=Code[certain tests].
|
||||
|
||||
- **High concurrency**: Write queries scale well up to the number of hardware execution threads in
|
||||
the server. Read queries never block each other.
|
||||
@@ -22,8 +22,8 @@ applications such as trading and financial market applications.
|
||||
servers, "last write wins" strategy by default, allows to implement custom https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type[state-based CRDT] strategy.
|
||||
|
||||
== Unique features
|
||||
- Multiple processes could access a Chronicle Map concurrently. At the same time,
|
||||
the data store is *in-process for each of the accessing processes*. Out-of-process approach to IPC
|
||||
- Multiple processes can access a Chronicle Map concurrently. At the same time,
|
||||
the data store is *in-process* for each of the accessing processes. Out-of-process approach to IPC
|
||||
is simply incompatible with Chronicle Map's median latency target of < 1 μs.
|
||||
|
||||
- Replication *without logs*, with constant footprint cost, guarantees progress even if the network
|
||||
@@ -51,24 +51,26 @@ queries*.
|
||||
=== Primary Chronicle Map use cases**
|
||||
|
||||
- Replacing slower key-value stores, like `Redis` and `Memcached`, when used within a single server.
|
||||
- Replacing similar JVM-centric solutions, like `Coherence` and `Hazelcast`, for speed and/or certain Chronicle Map features those solutions lack.
|
||||
- Moving parts of the application state out of the Java heap for either:
|
||||
* Reducing the heap size, for reducing GC pressure, or fitting 32 GB for using `CompressedOops`.
|
||||
|
||||
- Replacing similar JVM-centric solutions, like `Coherence` and `Hazelcast`, for speed and/or certain Chronicle Map features which those solutions lack.
|
||||
|
||||
- Moving parts of the application-state out of the Java heap for either:
|
||||
* Reducing the heap size, for reducing garbage collection pressure, or fitting 32 GB, for using `CompressedOops`.
|
||||
* Inter-process communication.
|
||||
* Persistence`
|
||||
* Persistence.
|
||||
* Replication across servers.
|
||||
|
||||
- Drop-in `ConcurrentHashMap` replacement, Chronicle Map performs better in some cases.
|
||||
- Drop-in `ConcurrentHashMap` replacement; Chronicle Map performs better in some cases.
|
||||
|
||||
=== What guarantees does Chronicle Map provide in ACID terms?
|
||||
|
||||
- Atomicity - single-key queries are atomic if Chronicle Map is properly configured, multi-key
|
||||
- **Atomicity** - single-key queries are atomic if Chronicle Map is properly configured, multi-key
|
||||
queries are not atomic.
|
||||
- Consistency - doesn't make sense for key-value stores
|
||||
- Isolation - yes (for both single- and multi-key queries).
|
||||
- Durability - no, Chronicle Map can be persisted to disk but with no guarantee as to to how frequently this
|
||||
happens - this is under the control of the OS. All data is guaranteed to be written to disk when the Map is closed.
|
||||
- Clustering and replication for Chronicle
|
||||
- **Consistency** - doesn't make sense for key-value stores
|
||||
- **Isolation** - yes; for both single- and multi-key queries.
|
||||
- **Durability** - no; Chronicle Map can be persisted to disk, but with no guarantee as to how frequently this
|
||||
happens. This is under the control of the OS. All data is guaranteed to be written to disk when the Map is closed.
|
||||
- **Clustering** and **replication** for Chronicle
|
||||
Map is provided by http://chronicle.software/products/chronicle-map/[Chronicle Map Enterprise].
|
||||
|
||||
**Project status: ready for production.**
|
||||
@@ -78,27 +80,29 @@ Simply put, a Chronicle Map data store is a big chunk of shared memory (optional
|
||||
|
||||
It is split into independent segments; each segment has:
|
||||
|
||||
- an independent memory allocation for storing the entries,
|
||||
- an independent memory allocation for storing the entries
|
||||
|
||||
- a hash table for search
|
||||
|
||||
- a lock in shared memory (implemented via CAS loops) for managing concurrent access.
|
||||
|
||||
Read [the Chronicle Map data store design overview](spec/2-design-overview.md) for more information.
|
||||
See the <<../spec/2-design-overview.md#, Chronicle Map data store design overview>> for more information.
|
||||
|
||||
== Chronicle Map is not
|
||||
|
||||
- A document store. No secondary indexes.
|
||||
- A document store. There are no secondary indexes.
|
||||
- A https://en.wikipedia.org/wiki/Multimap[multimap].
|
||||
Using a Chronicle Map collection as multimap is technically possible, but often leads to problems. See http://stackoverflow.com/a/36486525/648955[StackOverflow answer] for details.
|
||||
Developing a proper multimap with
|
||||
Chronicle Map's design principles is possible,
|
||||
Using a Chronicle Map collection as a multimap is technically possible, but often leads to problems. See http://stackoverflow.com/a/36486525/648955[StackOverflow answer] for details.
|
||||
Developing a proper multimap, using
|
||||
Chronicle Map's design principles is possible.
|
||||
|
||||
Please contact us at mailto:sales@chronicle.software[sales@chronicle.software] if
|
||||
you would consider sponsoring such development.
|
||||
|
||||
== Chronicle Map does not support:
|
||||
|
||||
- Range queries
|
||||
- Iteration over the entries in alphabetical order.
|
||||
- range queries.
|
||||
- iteration over the entries in alphabetical order.
|
||||
- sorting of keys.
|
||||
- LRU entry eviction.
|
||||
|
||||
@@ -129,7 +133,7 @@ Please contact us at mailto:sales@chronicle.software[sales@chronicle.software] i
|
||||
- https://github.com/OpenHFT/Chronicle-Engine[Chronicle Engine]. Reactive processing framework
|
||||
supporting Chronicle Map as a backend.
|
||||
- http://chronicle.software/products/chronicle-enterprise/[Chronicle Enterprise]. Extended
|
||||
version of Chronicle Engine.
|
||||
versions of Chronicle software.
|
||||
- http://vanillajava.blogspot.com/2015/09/chronicle-journal-customizable-data.html[Chronicle Journal]. Another
|
||||
key-value built by Chronicle Software, with different properties.
|
||||
|
||||
|
||||
+21
-15
@@ -27,50 +27,53 @@ image::http://openhft.net/wp-content/uploads/2014/07/Chronicle-Map-TCP-Replicati
|
||||
== TCP / UDP Background
|
||||
TCP/IP is a reliable protocol. This means that, unless you have a network failure or hardware outage, the data is guaranteed to arrive. TCP/IP provides point-to-point connectivity. For example, if the message was sent to 100 hosts, the message would have to be sent 100 times.
|
||||
|
||||
With UDP, the message is only sent once. This is ideal if you have a large number of hosts and you wish to broadcast the same data to each of them. However, one of the big drawbacks with UDP is that it's not a reliable protocol. This means, if the UDP message is broadcast onto the network, the hosts are not guaranteed to receive it; so they can miss data.
|
||||
With UDP, the message is only sent once. This is ideal if you have a large number of hosts, and you wish to broadcast the same data to each of them. However, one of the big drawbacks with UDP is that it is not a reliable protocol. This means, if the UDP message is broadcast onto the network, the hosts are not guaranteed to receive it; so they can miss data.
|
||||
|
||||
Some solutions attempt to build resilience into UDP, but arguably, this is in effect reinventing TCP/IP.
|
||||
|
||||
== How to setup UDP Replication
|
||||
On a good quality wired LAN, when using UDP, you will rarely miss messages. Nevertheless this is a risk that we suggest you don't take. We suggest that whenever you use UDP replication you use it in conjunction with throttled TCP replication. Therefore if a host misses a message over UDP, it will pick it up later via TCP/IP.
|
||||
On a good quality wired LAN, when using UDP, you will rarely miss messages. Nevertheless, this is a risk that we suggest you do not take. We suggest that whenever you use UDP replication, you use it in conjunction with throttled TCP replication. Therefore, if a host misses a message over UDP, it will pick it up later via TCP/IP.
|
||||
|
||||
== TCP/IP Throttling
|
||||
We are careful not to swamp your network with too much TCP/IP traffic. We do this by providing a throttled version of TCP replication. This works because Chronicle Map only broadcasts the latest update of each entry.
|
||||
|
||||
== How Chronicle Map Replication works
|
||||
Chronicle Map provides multi-master hash-map replication. This means that each remote map mirrors its changes over to another remote map. Neither map is considered the master store of data. Each map uses timestamps to reconcile changes.
|
||||
Chronicle Map provides multi-master hash-map replication. This means that each remote map mirrors its changes over to another remote map. Neither map is considered to be the master store of data. Each map uses timestamps to reconcile changes.
|
||||
|
||||
We refer to an instance of a remote map as a **node**. Each node can be connected to up to 128 other nodes.
|
||||
|
||||
The data that is stored locally in each node becomes eventually consistent. So changes made to one node, for example by calling `put()`, will be replicated over to the other node.
|
||||
|
||||
To achieve a high level of performance and throughput, the call to `put()` will not block,
|
||||
To achieve a high level of performance and throughput, the call to `put()` will not block.
|
||||
|
||||
With `ConcurrentHashMap`, it is typical to check the return code of some methods to obtain the old value; for example, `remove()`.
|
||||
|
||||
Due to the loose coupling and lock-free nature of this multi-master implementation, this return value is only the old value on the node's local data store. In other
|
||||
words, the nodes are only concurrent locally. It is worth understanding that another node, performing exactly the same operation, may return a different value. However, reconciliation will ensure that all the maps
|
||||
Due to the loose coupling, and lock-free nature of this multi-master implementation, the return value is only the old value on the node's local data store. In other
|
||||
words, the nodes are only concurrent locally. Another node, performing exactly the same operation, may return a different value. However, reconciliation will ensure that all the maps
|
||||
will become eventually consistent.
|
||||
|
||||
== Reconciliation
|
||||
If two or more nodes receive a change to their maps for the same key, but different values, say by a user of the maps, calling the `put(key,value)`, then, initially each node will update its local store, and each local store will hold a different value.
|
||||
If two or more nodes receive a change to their maps for the same key, but with different values, say by a user of the maps, calling the `put(key,value)`, then, initially each node will update its local store, and each local store will hold a different value.
|
||||
|
||||
The aim of multi-master replication is
|
||||
to provide eventual consistency across the nodes. So, with multi-master replication, whenever a node is changed it will notify the other nodes of its change; we refer to this notification as an event.
|
||||
to provide eventual consistency across the nodes. Using multi-master replication, whenever a node is changed it will notify the other nodes of its change; we refer to this notification as an event.
|
||||
|
||||
The event will hold a timestamp indicating the time that the change occurred. It will also hold the state transition; in this case it was a `put` with a key and value.
|
||||
The event will hold a timestamp indicating the time that the change occurred. It will also hold the state transition; in this case it was a `put` with a key and value.
|
||||
|
||||
Eventual consistency is achieved by looking at the timestamp from the remote node. If, for a given key, the remote node's timestamp is newer than the local node's timestamp, then the event from the remote node will be applied to the local node; otherwise, the event will be ignored.
|
||||
|
||||
Since none of the nodes is a primary, each node holds information about the other nodes. For this node its own identifier is referred to as its 'localIdentifier'. The identifiers of other nodes are the 'remoteIdentifiers'.
|
||||
Since none of the nodes is a primary, each node holds information about the other nodes. For a specific node, its own identifier is referred to as the 'localIdentifier'. The identifiers of other nodes are the 'remoteIdentifiers'.
|
||||
|
||||
On an update, or insert of a key/value, this node pushes the information about the change to the remote nodes. The nodes use non-blocking Java NIO I/O, and all replication is done on a single thread.
|
||||
On an update, or insert, of a key/value, the node pushes the information about the change to the remote nodes. The nodes use non-blocking Java NIO I/O, and all replication is done on a single thread.
|
||||
|
||||
However, there is an edge case. If two nodes update their map at the same time with different values, we have to deterministically resolve which update wins. This is because eventual
|
||||
However, there is a specific edge case. If two nodes update their map at precisely the same time with different values, we have to deterministically resolve which update wins. This is because eventual
|
||||
consistency mandates that both nodes should end up holding the same data locally.
|
||||
Although it is rare that two remote
|
||||
|
||||
Although it is rare that two remote
|
||||
nodes receive an update to their maps at exactly the same time, for the same key, we have to handle this edge case. We cannot therefore rely on timestamps alone to reconcile
|
||||
the updates. Typically, the update with the newest timestamp should win, but in this example both timestamps are the same, and the decision made to one node should be identical to the decision made to the other. This dilemma is resolved by using a node identifier. The node identifier is a unique
|
||||
the updates.
|
||||
|
||||
Typically, the update with the newest timestamp should win, but in this example both timestamps are the same, and the decision made to one node should be identical to the decision made to the other. This dilemma is resolved by using a node identifier. The node identifier is a unique
|
||||
'byte' value that is assigned to each node. When the timestamps are the same, the remote node with the smaller identifier will be preferred.
|
||||
|
||||
== Multiple Processes on the same server with Replication
|
||||
@@ -85,6 +88,7 @@ Likewise, if the TCP replicated Chronicle Map instance received an update from a
|
||||
If you are only replicating your Chronicle Map instances on the same server, then you do not have to set up TCP and UDP replication. You also do not have to set the identifiers; as the identifiers are only used for the resolution of conflicts amongst remote servers.
|
||||
|
||||
If however, you wish to replicate data between two or more servers, then all of the Chronicle Map instances, including those not actively participating in TCP or UDP replication, must have their identifiers set.
|
||||
|
||||
The identifier must be unique to each server. Each ChronicleMap on the same server must have
|
||||
the same identifier. The reason that all Chronicle Map instances must have the identifier set, is because
|
||||
the memory is laid out slightly differently when using replication, so even if a map is not actively performing TCP or UDP replication itself, if it wishes to replicate with one that is, it must have its memory laid out in the same way to be compatible.
|
||||
@@ -106,8 +110,9 @@ map = ChronicleMapBuilder
|
||||
|
||||
== Configuration
|
||||
|
||||
Configuration of map nodes is done either programmatically, through configuration files, or a combination of both.
|
||||
Configuration of map nodes is done either, programmatically, through configuration files, or by a combination of both.
|
||||
|
||||
=== Programmatically
|
||||
The example below shows how to set up two map instances that use TCP to replicate entries between them:
|
||||
|
||||
[source, java]
|
||||
@@ -152,6 +157,7 @@ System.out.printf("mapOne has %d entries, expected 200%n", mapOne.size());
|
||||
System.out.printf("mapTwo has %d entries, expected 200%n", mapTwo.size());
|
||||
....
|
||||
|
||||
=== Configuration file
|
||||
The following example uses a basic `yaml` configuration file to define defaults for the map:
|
||||
|
||||
[source, yaml]
|
||||
|
||||
@@ -13,17 +13,17 @@ NOTE: This document assumes that you have access to the restricted link::https:/
|
||||
|
||||
== Single master, multiple slaves
|
||||
|
||||
To execute a test, open the the `Example4` directory, and then execute the following command from a `bash` shell:
|
||||
To execute a test, open the the `Example4` folder, and then execute the following command from a `bash` shell:
|
||||
|
||||
[source,bash]
|
||||
....
|
||||
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml
|
||||
....
|
||||
|
||||
This command will run a test using the specified configuration, which models a single master
|
||||
node along with three slave nodes. All nodes run within the same JVM for simplicity.
|
||||
This command runs a test, using the specified configuration, which models a single master
|
||||
node working with three slave nodes. All nodes run within the same JVM for simplicity.
|
||||
|
||||
The test will report throughput metrics in terms of reads, writes, and network traffic. For example:
|
||||
The test reports throughput metrics in terms of reads, writes, and network traffic. For example:
|
||||
|
||||
[source,bash]
|
||||
....
|
||||
@@ -35,7 +35,7 @@ Node 3 received 1061 events/sec, 151KB/sec from remote 1
|
||||
Node 2 received 1062 events/sec, 151KB/sec from remote 1
|
||||
....
|
||||
|
||||
Every minute, the test will pause the writer and check that all maps within the cluster
|
||||
Every minute, the test pauses the writer, and checks that all maps within the cluster
|
||||
contain the same data; that is, they all have the same `keySet`, and the value for each key is
|
||||
identical throughout the cluster. For example:
|
||||
|
||||
@@ -51,26 +51,29 @@ All maps contained the same data
|
||||
|
||||
To spread the workload over multiple JVMs, use the `-h` parameter, which specifies the nodes that should be started in a particular JVM. The `singleMasterStressTest.yaml` configuration file contains 4 nodes (1 master, 3 slaves). The example below spreads these nodes across two processes.
|
||||
|
||||
In one terminal, run the following command:
|
||||
. On one terminal, run the following command:
|
||||
|
||||
[source,bash]
|
||||
....
|
||||
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 1 -h 2
|
||||
....
|
||||
[source,bash]
|
||||
....
|
||||
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 1 -h 2
|
||||
....
|
||||
|
||||
+
|
||||
then wait for the message notifying that the test is starting:
|
||||
|
||||
[source,bash]
|
||||
....
|
||||
Built stress test artifacts, running test..
|
||||
....
|
||||
[source,bash]
|
||||
....
|
||||
Built stress test artifacts, running test..
|
||||
....
|
||||
|
||||
Now in another terminal, run the following command:
|
||||
. On another terminal, run the following command:
|
||||
|
||||
[source,bash]
|
||||
....
|
||||
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3 -h 4
|
||||
....
|
||||
[source,bash]
|
||||
....
|
||||
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3 -h 4
|
||||
....
|
||||
|
||||
+
|
||||
|
||||
In both terminals, throughput and map-equality checking will be reported.
|
||||
|
||||
@@ -114,6 +117,7 @@ Test parameters are described below:
|
||||
== Running tests across multiple physical machines
|
||||
|
||||
To demonstrate how to run tests across different machines, the following details are defined:
|
||||
|
||||
Hosts within the cluster:
|
||||
|
||||
[frame="topbot",options="header,footer"]
|
||||
@@ -128,11 +132,11 @@ Hosts within the cluster:
|
||||
NOTE: Substitute your own settings as appropriate.
|
||||
|
||||
|
||||
TCP port used for replication; this port will need to be open on all hosts:
|
||||
**TCP port used for replication**; this port will need to be open on all hosts:
|
||||
|
||||
`13000`
|
||||
|
||||
Configuration file name:
|
||||
**Configuration file name**:
|
||||
|
||||
`multiHost.yaml`
|
||||
|
||||
@@ -193,7 +197,7 @@ To run the test on remote machines, the stress test can be packaged up using the
|
||||
$ ./package_stress_test.sh
|
||||
....
|
||||
|
||||
This command will generate a `tar` archive in the current directory, containing everything required to run a stress test.
|
||||
This command generates a `tar` archive in the current directory, containing everything required to run a stress test.
|
||||
|
||||
Copy the resulting `tar` file to the remote server, unpack using `tar xf`, and then run the following command:
|
||||
|
||||
@@ -207,10 +211,10 @@ To run the example multi-machine configuration described above, the following co
|
||||
[frame="topbot",options="header,footer"]
|
||||
|=======================================
|
||||
| host | host_id | IP | command
|
||||
| A | 1 |10.0.2.1 | ./run_stress_test.sh -c multiHost.yaml -h 1
|
||||
| B | 2 |10.0.2.14 | ./run_stress_test.sh -c multiHost.yaml -h 2
|
||||
| C | 3 |10.0.2.30 | ./run_stress_test.sh -c multiHost.yaml -h 3
|
||||
| D | 4 |10.0.2.22 | ./run_stress_test.sh -c multiHost.yaml -h 4
|
||||
| **A** | `1` |`10.0.2.1` | `./run_stress_test.sh -c multiHost.yaml -h 1`
|
||||
| **B** | `2` |`10.0.2.14` | `./run_stress_test.sh -c multiHost.yaml -h 2`
|
||||
| **C** | `3` |`10.0.2.30` | `./run_stress_test.sh -c multiHost.yaml -h 3`
|
||||
| **D** | `4` |`10.0.2.22` | `./run_stress_test.sh -c multiHost.yaml -h 4`
|
||||
|=======================================
|
||||
|
||||
|
||||
@@ -218,8 +222,9 @@ To run the example multi-machine configuration described above, the following co
|
||||
|
||||
There are two ways of recovering from node failure:
|
||||
|
||||
1. Wipe previous map data, and rejoin the cluster
|
||||
2. Recover from previous map data, and rejoin the cluster
|
||||
1. Delete previous map data, and rejoin the cluster.
|
||||
|
||||
2. Recover previous map data, and rejoin the cluster.
|
||||
|
||||
If the total dataset contains a large proportion of infrequently updated records, then it is preferable to use option 2.
|
||||
|
||||
@@ -243,7 +248,7 @@ Both scenarios can be simulated using the stress test.
|
||||
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 1
|
||||
....
|
||||
|
||||
. When replication has been established, kill the slave process, (for example by using `Ctrl+c`).
|
||||
. When replication has been established, kill the slave process; for example, by using `Ctrl+c`.
|
||||
|
||||
. Restart the slave process (see step 1).
|
||||
By default, the stress test code deletes existing map data files for the specified hosts
|
||||
@@ -256,7 +261,7 @@ on startup.
|
||||
|
||||
+
|
||||
There should be a noticable difference in throughput after the restart, as
|
||||
the slave map gets pushed the entire data-set from the master:
|
||||
the slave map receives the entire data-set from the master:
|
||||
|
||||
[source,bash]
|
||||
....
|
||||
@@ -281,6 +286,6 @@ Perform the same procedure as in Option 1, step 4, but this time when restarting
|
||||
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3 -r
|
||||
....
|
||||
|
||||
This will ensure that the existing map is recovered from disk, possibly saving network traffic, depending on how much data has been updated in the master map during downtime.
|
||||
This ensures that the existing map is recovered from disk. This may save network traffic, depending on how much data has been updated in the master map during downtime.
|
||||
|
||||
<<CM_Replication.adoc#,Back to Replication>>
|
||||
@@ -12,22 +12,20 @@ Changes between Chronicle Map 3 and the previous Chronicle Map version are detai
|
||||
|
||||
== Functional changes
|
||||
|
||||
- Chronicle Map 3 has link::https://github.com/OpenHFT/Chronicle-Map/blob/doco_refresh/spec[formal data store specification] that specifies link::https://github.com/OpenHFT/Chronicle-Map/blob/doco_refresh/spec/1-design-goals.md#guarantees-1[the guarantees
|
||||
- Chronicle Map 3 has a link::https://github.com/OpenHFT/Chronicle-Map/blob/doco_refresh/spec[formal data store specification] that specifies link::https://github.com/OpenHFT/Chronicle-Map/blob/doco_refresh/spec/1-design-goals.md#guarantees-1[the guarantees
|
||||
which the data store provides] and gives a way to verify those guarantees.
|
||||
|
||||
- Added support for multi-key queries.
|
||||
|
||||
- "Listeners" mechanism fully reworked; see <<CM_Tutorial_Behaviour.adoc#,Behaviour Customization>>. This has a number of important consequences, most notable is:
|
||||
|
||||
** it is now possible to define "eventual-consistency" replication strategy, different from "last write wins"; any state-based CRDT.
|
||||
- "Listeners" mechanism fully reworked; see <<CM_Tutorial_Behaviour.adoc#,Behaviour Customization>>. This has a number of important consequences, most notable is that it is now possible to define "eventual-consistency" replication strategy, different from "last write wins"; any state-based CRDT.
|
||||
|
||||
- "Stateless clients" functionality (remote calls) is moved to https://github.com/OpenHFT/Chronicle-Engine[Chronicle Engine].
|
||||
|
||||
- Replication is performed using https://github.com/OpenHFT/Chronicle-Engine[Chronicle Engine].
|
||||
|
||||
- Earlier versions of Chronicle Map had hard creation-time limits on the number of entries storable in a Chronicle Map. If the size exceeded this limit, an exception is thrown. +
|
||||
- Earlier versions of Chronicle Map had hard creation-time limits on the number of entries storable in a Chronicle Map. If the size exceeded this limit, an exception was thrown. +
|
||||
In Chronicle Map 3, this limitation is removed. The number of entries still has to be configured when the Chronicle Map is created, Exceeding this configured limit is possible, but discouraged. +
|
||||
For more information, see the <<Configure `.entries(entries)`>> section.
|
||||
For more information, see <<CM_Tutorial.adoc#Configure `.entries(entries)`,Configure `.entries(entries)`>>.
|
||||
|
||||
- Chronicle Map 3 supports <<CM_Tutorial.adoc#entry-checksums,entry checksums>> that enables detection of data corruption, providing additional safety.
|
||||
|
||||
@@ -37,9 +35,9 @@ Changes between Chronicle Map 3 and the previous Chronicle Map version are detai
|
||||
|
||||
== Non-functional changes
|
||||
|
||||
- Chronicle Map 3 requires Java version 8, or newer.
|
||||
- Chronicle Map 3 requires Java version 8, or later.
|
||||
|
||||
- Chronicle Map 3 has <<CM_Versioning.adoc#, versioning policy>> and <<CM_Compatibility.adoc#,compatibility policy>>.
|
||||
- Chronicle Map 3 has <<CM_Versioning_and_Compatibility.adoc#,versioning and compatibility policies>>.
|
||||
|
||||
|
||||
'''
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
= Chronicle Map Compatibility
|
||||
Neil Clifford
|
||||
:toc: macro
|
||||
:toclevels: 1
|
||||
:css-signature: demo
|
||||
:toc-placement: macro
|
||||
:icons: font
|
||||
|
||||
toc::[]
|
||||
|
||||
== Java Implementation Compatibility
|
||||
|
||||
=== Public API
|
||||
|
||||
The *public API* comprises public classes that are included in
|
||||
http://www.javadoc.io/doc/net.openhft/chronicle-map/[Javadoc].
|
||||
|
||||
Note: Other public classes are not subject to
|
||||
the following rules; they may be changed in any way, or even removed.
|
||||
|
||||
- Class and method names, types and number of parameters in already existing methods shouldn't be
|
||||
changed.
|
||||
- Any methods could be added to the classes and interfaces that are not subject for
|
||||
subclassing or implementing by the library users, such as `ChronicleMap`, `ChronicleMapBuilder`,
|
||||
`MapEntry`, but in a way that any existing code calling methods on those classes still compile, not
|
||||
break due to ambiguity in method resolution.
|
||||
- Only non-abstract (concrete or default) methods could be added to the classes and interfaces that
|
||||
are subject for subclassing by the library users, such as `MapMethods`, `MapEntryOperations`,
|
||||
`SizedReader`.
|
||||
|
||||
=== Behaviour
|
||||
|
||||
Semantics and actual effects of API calls might change between Chronicle Map versions with
|
||||
[different `<minor>` number](versioning.md), e. g. default serialization forms of key or value objects
|
||||
of some types could change, "high level" configurations in `ChronicleMapBuilder` could produce
|
||||
Chronicle Maps with different "low level" configurations, etc. Such changes should be documented in the release notes.
|
||||
|
||||
=== Persistence File Binary Form
|
||||
|
||||
A persisted Chronicle Map store (a binary file), produced by a production release of the library,
|
||||
should be accessible and updatable by any subsequent production release of the library in 3.x
|
||||
branch, unless the older version has bugs that lead to data corruption.
|
||||
|
||||
To support this:
|
||||
|
||||
- Serialization forms of any classes shouldn't be changed. When e. g. a marshaller class (say,
|
||||
`StringBytesReader`) of objects of some type is changed, and it's serialization form should be
|
||||
changed, a new class called `StringBytesReader_3_5` (5 is the `<minor>` number of the version of the
|
||||
library, in which this change is made) should be created and put along with older class.
|
||||
|
||||
- When the specification revision is updated (hence binary form could be completely changed), a
|
||||
new version of root implementation class, e. g. `VanillaChronicleMap_3_5` should be created and
|
||||
put along with older version, which should keep behave the same as before.
|
||||
|
||||
=== Replication Protocol
|
||||
|
||||
The replication protocol might change between Chronicle Map versions with different `<minor>` number.
|
||||
Chronicle Map nodes running different versions of the library should not communicate to each other.
|
||||
This may lead to failures, but not to data corruption on either of the nodes.
|
||||
|
||||
== Specification Compatibility
|
||||
|
||||
The Chronicle Map Data Store Specification could be updated without changing the revision while it
|
||||
remains backward compatible, i. e. the implementation of the newer version of the specification
|
||||
could access and update any persisted Chronicle Map store, produced by an older version.
|
||||
In addition, potential *concurrent* accessing implementations of older versions ("within" the same
|
||||
revision) shouldn't lose correctness and corrupt the data.
|
||||
|
||||
If more substantial changes to the specification are required, the revision number should be
|
||||
updated.
|
||||
|
||||
The binary form of Chronicle Map and algorithms could change between revisions to any degree, but
|
||||
[guarantees](../spec/1-design-goals.md#guarantees) provided by the specification shouldn't be
|
||||
weaken.
|
||||
|
||||
'''
|
||||
<<../ReadMe.adoc#,Back to ReadMe>>
|
||||
@@ -1,69 +0,0 @@
|
||||
= Chronicle Map 3 Versioning
|
||||
Neil Clifford
|
||||
:toc: macro
|
||||
:toclevels: 1
|
||||
:css-signature: demo
|
||||
:toc-placement: macro
|
||||
:icons: font
|
||||
|
||||
toc::[]
|
||||
|
||||
== Java implementation Versioning
|
||||
|
||||
The versioning format is as follows:
|
||||
|
||||
`<major>.<minor>.<patch>`
|
||||
|
||||
For example:
|
||||
`3.14.1`
|
||||
|
||||
|
||||
=== The <minor> number should be updated, when:
|
||||
|
||||
- a feature, or public API method is added.
|
||||
|
||||
- the behaviour of `ChronicleMap`, `ChronicleMapBuilder` or any other class or method or subsystem
|
||||
is semantically changed.
|
||||
For example, when configuration heuristics in `ChronicleMapBuilder` are improved, and
|
||||
`ChronicleMapBuilder` configured the same way constructs a `ChronicleMap` with a different
|
||||
number of segments, or segment's capacity, etc.
|
||||
|
||||
- any dependency is updated, even if only `<patch>` number is updated, e. g. `chronicle-bytes`
|
||||
dependency is updated from `1.1.1` to `1.1.2`. This rule doesn't apply to dependencies to the umbrella POMs which merely determine versions of other dependencies (namely `chronicle-bom` and
|
||||
`third-party-bom`), if the versions of actual dependencies don't change between those versions of umbrella POMs. Also, this rule doesn't apply to dependencies with Maven scopes of `test` or `provided`.
|
||||
|
||||
- bug fixes, performance optimization or feature addition requires to change the binary or
|
||||
serialized form of Chronicle Map data store (therefore revise the Chronicle Map data store
|
||||
specification, see below), or the binary replication protocol, or the serialized form of some
|
||||
marshaller classes, that requires to [create new versions of classes](compatibility.md).
|
||||
|
||||
=== The <minor> number should not be updated, i. e. the next release could update only the "patch" number, when
|
||||
:
|
||||
- A bug fixed, in a way that doesn't make any working code to change its behaviour semantically.
|
||||
- Performance optimized: some working code changes its performance characteristics,
|
||||
but not semantics
|
||||
|
||||
During development after the release of the version `3.y.z` the library should be versioned
|
||||
`3.y.<x+1>-SNAPSHOT`. As soon as changes made are substantial enough for the next released version
|
||||
to update the "minor" number, the version should be changed to `3.<y+1>.0-SNAPSHOT`, *in the same
|
||||
commit* these changes are made.
|
||||
|
||||
Suffixes `-alpha`, `-beta`, `-rc` etc. mean this is a pre-production release.
|
||||
|
||||
== Specification Versioning
|
||||
|
||||
The first Chronicle Map 3 production release designates the Chronicle Map data store specification, revision 1.
|
||||
|
||||
When the specification should be updated in a way that is not [backward compatible](compatibility.md#the-specification-compatibility), the revision should be updated (revision 2, 3, ...) and the Java implementation of this revision should have '<minor>' number updated in its
|
||||
version.
|
||||
|
||||
During the development after the release of the revision `x`, while the specification is backward
|
||||
compatible, it should remain versioned `x`. In the same commit where the specification becomes
|
||||
incompatible, its revision should be set to `<x+1>-pre` until the next production release.
|
||||
|
||||
Releases and updates to the `<minor>` number in the library version should be synchronized with
|
||||
updates to the specification revision number.
|
||||
|
||||
|
||||
'''
|
||||
<<../ReadMe.adoc#,Back to ReadMe>>
|
||||
Reference in New Issue
Block a user