TeamCity Automatic Merge from master into feature/java11

This commit is contained in:
Team City
2018-09-26 14:50:28 +01:00
3 changed files with 58 additions and 396 deletions
+1
View File
@@ -1,6 +1,7 @@
### How to update
# This is copied from OpenHFT/.gitignore
# update the original and run OpenHFT/update_gitignore.sh
data/
### Compiled class file
*.class
+57 -112
View File
@@ -18,26 +18,15 @@ In addition, you will be fully supported by our technical experts.
For more information on Chronicle Map (Enterprise Edition), please contact mailto:sales@chronicle.software[sales@chronicle.software].
To ensure that Chronicle Map's multi-master replication works under extreme circumstances, we have
built a stress test to ensure this using the principles introduced by Netflix with their "chaos monkey".
For more information on the stress test see <<CM_Replication_Stress_Test.adoc#,CM_Replication_Stress_Test>>.
= Replication
Chronicle Map Replication is a multi-master replicated data store. Chronicle Map supports both TCP and UDP replication
Chronicle Map Replication is a multi-master replicated data store. Chronicle Map supports replication over TCP/IP
image::http://openhft.net/wp-content/uploads/2014/07/Chronicle-Map-TCP-Replication_simple_02.jpg[TCP/IP Replication]
== TCP / UDP Background
== TCP 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 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 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.
@@ -114,79 +103,69 @@ map = ChronicleMapBuilder
== Configuration
Configuration of map nodes is done either, programmatically, through configuration files, or by a combination of both.
Configuration of map nodes is done either, by creating configuration programmatically, or through YAML configuration files.
=== Programmatically
The example below shows how to set up two map instances that use TCP to replicate entries between them:
[source, java]
....
// set up some socket aliases
TCPRegistry.createServerSocketChannelFor("host.port1", "host.port2");
// create replicated maps
final ReplicatedMap<Long, Long> replicaOne = createMap(1, "host.port1");
final ReplicatedMap<Long, Long> replicaTwo = createMap(2, "host.port2");
// start replication event loops
replicaOne.start();
replicaTwo.start();
// register maps for replication events
replicaOne.beginReplicationFromPeer("host.port2", (byte) 2);
replicaTwo.beginReplicationFromPeer("host.port1", (byte) 1);
// allow replication to establish
Jvm.pause(250L);
// access the underlying maps
final Map<Long, Long> mapOne = replicaOne.getMap();
final Map<Long, Long> mapTwo = replicaTwo.getMap();
// enter some data into mapOne
for (long i = 0; i < 100; i++) {
mapOne.put(i, i * 2);
}
// enter some different data into mapTwo
for (long i = 100; i < 200; i++) {
mapTwo.put(i, i * 7);
}
// wait for replication between nodes (conservative)
Jvm.pause(1000L);
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:
The following example uses a basic `yaml` configuration file to define clustered replication for the map named `fx`:
[source, yaml]
....
!ReplicatedMapCfg {
hostId: 1, # host id must be unique within the cluster
listenAddressHostPort: "localhost:9301", # address on which to listen for inbound replication requests
entries: 10000, # entries hint suggesting the initial size of the map
keyClass: !type long, # class of the key type
valueClass: !type long, # class of the value type
exampleKey: 17, # an example key (used to size the map)
exampleValue: 37, # an example value (used to size the map)
name: testLongToLongMap, # a name for the map (used to determine filename)
locator: "csp://localhost:9301/map/test", # a locator, used for discovery
mapFileDataDirectory: target/mapData, # directory where map file should be stored
mapLogDirectory: target/mapReplicationLogs, # directory where replication events should be recorded
keysAreConstantSize: true, # determines how keys are serialised
valuesAreConstantSize: true # determines how values are serialised
!MapReplicationCfg {
cluster: {
host1: {
hostId: 1,
connectUri: hostport1,
},
host2: {
hostId: 2,
connectUri: hostport2,
},
host3: {
hostId: 3,
connectUri: hostport3,
}
},
maps: {
fx: {
entries: 10000,
keyClass: !type String,
valueClass: !type software.chronicle.enterprise.map.ValueObject,
averageKeySize: 64,
averageValueSize: 128,
mapFileDataDirectory: data/$hostId/,
mapLogDirectory: logs/$hostId/,
enableReplicationLogging: true
},
}
}
....
After putting 100 entries into each distinct map, the example demonstrates that the entries have been replicated so that each map
now contains 200 entries.
And below is an example using this configuration file to start up cluster and insert entries in different maps, verifying that
all maps are eventually in sync:
This example is available in the repository, https://github.com/ChronicleEnterprise/Chronicle-Map-Enterprise/blob/master/src/test/java/software/chronicle/enterprise/map/example/MultiMasterMapReplicationExampleMain.java[MultiMasterMapReplicationExampleMain.java]
[source, java]
....
try (ReplicatedMap clusterOnHost1 = createCluster(CLUSTER_YAML, 1);
ReplicatedMap clusterOnHost3 = createCluster(CLUSTER_YAML, 3);
ReplicatedMap clusterOnHost2 = createCluster(CLUSTER_YAML, 2)) {
final ChronicleMap<String, ValueObject> mapOnHost1 = clusterOnHost1.getReplicatedMap("fx");
final ChronicleMap<String, ValueObject> mapOnHost2 = clusterOnHost2.getReplicatedMap("fx");
final ChronicleMap<String, ValueObject> mapOnHost3 = clusterOnHost3.getReplicatedMap("fx");
mapOnHost1.put("USD/GBP", new ValueObject("BATS", System.currentTimeMillis(), 0.767957));
mapOnHost2.put("GBP/USD", new ValueObject("BATS", System.currentTimeMillis(), 1.30216));
mapOnHost3.put("EUR/USD", new ValueObject("LXN", System.currentTimeMillis(), 1.16337));
Jvm.pause(500L);
printMap("one", mapOnHost1);
printMap("two", mapOnHost2);
printMap("three", mapOnHost3);
}
....
This example is available in the repository https://github.com/ChronicleEnterprise/Chronicle-Map-Enterprise-Demo/blob/master/Example1/src/main/java/software/chronicle/enterprise/map/ThreeWayMapReplicationExampleMain.java[here]
== Replication event logging
@@ -200,44 +179,10 @@ The example below shows the message flow for a map with a single remote peer rec
....
.. header omitted
targetHostId: 2 # message destination
registerForReplication: {
receiverHostId: 1, # local peer id
keyClassName: java.lang.Long, # key type
valueClassName: java.lang.Long, # value type
sendAllEventsAfter: 0 # bootstrap timestamp
}
..
== corresponding registration message from remote peer
targetHostId: 1
registerForReplication: {
receiverHostId: 2,
keyClassName: java.lang.Long,
valueClassName: java.lang.Long,
sendAllEventsAfter: 0
}
..
== accept replication connection
targetHostId: 2
acceptingReplicationRequest: {
acceptingHostId: 1
}
..
== encoded replication update sent to remote peer
targetHostId: 2
replicatedEntry: !!binary AYChq5LqwKXqFAFOEwAAAAAAAAD/////////fw==
....
This output can be generated by running the example program, https://github.com/ChronicleEnterprise/Chronicle-Map-Enterprise/blob/master/src/test/java/software/chronicle/enterprise/map/example/ReplicationEventLoggingExampleMain.java[ReplicationEventLoggingExampleMain.java]
'''
For the replication stress test, see <<CM_Replication_Stress_Test.adoc#,CM_Replication_Stress_Test>>.
'''
<<CM_Features.adoc#,Back to Features>>
-284
View File
@@ -1,284 +0,0 @@
= Replication Stress test
:toc: macro
:toclevels: 1
:css-signature: demo
:toc-placement: macro
:icons: font
toc::[]
The following stress test is provided for Chronicle Map Enterprise Edition. The test can be configured to run across multiple machines, with varying read/write workloads.
NOTE: This document assumes that you have access to the restricted https://github.com/ChronicleEnterprise/Chronicle-Map-Enterprise-Demo[Chronicle-Map-Enterprise-Demo] repository.
== Single master, multiple slaves
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 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 reports throughput metrics in terms of reads, writes, and network traffic. For example:
[source,bash]
....
Write stress-test-1: 237 inserts/sec, 702 updates/sec (total 940/sec)
Read stress-test-2: 2175 reads/sec, 0.00% were null
Read stress-test-3: 2177 reads/sec, 0.00% were null
Read stress-test-4: 2176 reads/sec, 0.00% were null
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 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:
[source,bash]
....
Starting map checker due to time of 11:37:30.035
Checking maps for equality
All maps contained the same data
....
== Running the test in multiple JVMs
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.
. On one terminal, run the following command:
[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..
....
. On another terminal, run the following command:
[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.
== Multiple masters, multiple slaves
To test a multi-master scenario, where several different nodes are written to, execute the following command:
[source,bash]
....
./run_stress_test.sh -c src/main/resources/multipleMasterStressTest.yaml
....
== Test configuration
Stress test scenarios are configured using the supplied `yaml` file. Using the examples
included in this repository as a template, it is possible to configure custom scenarios
to model real-world workloads.
The configuration is comprised of two components:
1. Test parameters
2. Cluster configuration
Test parameters are described below:
[source,yaml]
....
!TestAndClusterConfig {
testConfig: !StressTestConfig {
numberOfSlaveNodes: 3, # map instances that are read from
numberOfMasterNodes: 1, # map instances that are written to
updatesPerSecond: 1000, # writes per instance per second
queriesPerSecond: 2500, # reads per instance per second
insertToUpdateRatio: 0.25, # ratio of remove/insert vs update operations
keySpaceSize: 5000, # size of the key space (uniform random integers)
valueSizeInBytes: 128, # payload size of value object
testLengthMinutes: 2 # duration of a test
},
....
== 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"]
|=======================================
| host | host_id | IP | role
| A | 1 |10.0.2.1 | master
| B | 2 |10.0.2.14 | master
| C | 3 |10.0.2.30 | slave
| D | 4 |10.0.2.22 | slave
|=======================================
NOTE: Substitute your own settings as appropriate.
**TCP port used for replication**; this port will need to be open on all hosts:
`13000`
**Configuration file name**:
`multiHost.yaml`
The cluster configuration in `multiHost.yaml` would be:
[source,yaml]
....
clusterConfig: !Clusters {
myCluster: {
config: !ReplicatedMapCfg {
entries: 10000,
keyClass: !type long,
exampleKey: 17,
name: stress-test-map,
keysAreConstantSize: true,
valuesAreConstantSize: false,
keyDataAccess: !LongDataAccess {
},
keyReader: !LongMarshaller {
},
},
topology: {
context: !MapClusterContext {
wireType: TEXT,
},
hostA: {
hostId: 1,
connectUri: "10.0.2.1:13000"
},
hostB: {
hostId: 2,
connectUri: "10.0.2.14:13000"
},
hostC: {
hostId: 3,
connectUri: "10.0.2.30:13000"
},
hostD: {
hostId: 4,
connectUri: "10.0.2.22:13000"
}
},
monitoring: {
replicationEventListener: !LoggingReplicationEventListener
}
}
....
=== Packaging the test
To run the test on remote machines, the stress test can be packaged up using the following command:
[source,bash]
....
$ ./package_stress_test.sh
....
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:
[source,bash]
....
./run_stress_test.sh -c [config-file]
....
To run the example multi-machine configuration described above, the following commands would be run on each host:
[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`
|=======================================
== Destructive testing
There are two ways of recovering from node failure:
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.
If the entire key-set is updated frequently, then there is little difference between the two methods.
Both scenarios can be simulated using the stress test.
=== Delete existing data and rejoin (Option 1)
. Start one or more slave processes:
[source,bash]
....
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3
....
. Start one or more master nodes:
[source,bash]
....
$ ./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`.
. Restart the slave process (see step 1).
By default, the stress test code deletes existing map data files for the specified hosts
on startup.
[source,bash]
....
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3
....
+
There should be a noticable difference in throughput after the restart, as
the slave map receives the entire data-set from the master:
[source,bash]
....
# usual operation
Node 3 received 1153 events/sec, 153KB/sec from remote 1
# slave process killed and restarted
# higher throughput as slave catches up
Node 3 received 1646 events/sec, 234KB/sec from remote 1
....
=== Recover existing data and rejoin (Option 2)
The stress test can be configured, using the `-r` parameter, to recover from existing data on startup.
Perform the same procedure as in Option 1, step 4, but this time when restarting the slave client use `-r`:
[source,bash]
....
$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3 -r
....
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>>