diff --git a/README.md b/README.md
index 8cb0a0a84..923a3952d 100644
--- a/README.md
+++ b/README.md
@@ -1,133 +1,158 @@
-*We can help you get Chronicle up and running in your organisation, we suggest you invite us in for
-consultancy, charged on an ad-hoc basis, we can discuss the best options tailored to your individual
-requirements. - [Contact Us](http://chronicle.software/support/)*
-
-*Or you may already be using Chronicle and just want some help - [find out more..](http://chronicle.software/support/)*
-
# Chronicle Map
-A low latency replicated Key Value Store across your network, with eventual consistency, persistence and performance.
-
+
-#### Maven Artifact Download
-```xml
-
- net.openhft
- chronicle-map
-
-
-```
-Click here to get the [Latest Version Number](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.openhft%22%20AND%20a%3A%22chronicle-map%22)
+
+
+### 3 min to understand everything about Chronicle Map
+
+Chronicle Map is an in-memory key-value store designed for low-latency and/or multi-process
+applications. Notably trading, 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 [certain tests](
+ https://github.com/OpenHFT/Chronicle-Map/search?l=java&q=perf&type=Code).
+ - *High concurrency:* write queries scale well up to the number of hardware execution threads in
+ the server. Read queries never block each other.
+ - (Optional) persistence to disk.
+ - (Optional) eventually-consistent, fully-redundant, asynchronous replication across servers,
+ "last write wins" strategy by default, allows to implement custom [state-based CRDT](
+ https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) strategy.
+ - Multi-key queries.
+
+**Unique features**
+ - Multiple processes could access a Chronicle Map instance concurrently. At the same time,
+ the instance 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
+ doesn't sustain write rates.
+
+Chronicle Map has two meanings: [the language-agnostic data structure](
+https://github.com/OpenHFT/Chronicle-Map/tree/master/spec) and [the implementation of this data
+structure for the JVM](https://github.com/OpenHFT/Chronicle-Map/tree/master/src). Currently, this is
+the only implementation.
+
+**From Java perspective,** Chronicle Map is a `ConcurrentMap` implementation which stores the
+entries *off-heap*, serializing/deserializing key and value objects to/from off-heap memory
+transparently. Chronicle Map supports
+ - Key and value objects caching/reusing for making *zero allocations (garbage) on
+queries*.
+ - Flyweight values for eliminating serialization/deserialization cost and allowing direct
+ read/write access to off-heap memory.
+
+**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 Compressed Oops
+ - Inter-process communication
+ - Persistence
+ - Replication across servers
+ - 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
+ queries are not atomic.
+ - Consistency - doesn't make sense for key-value stores
+ - Isolation - yes (for both single- and multi-key queries).
+ - Durability - no, at most, Chronicle Map could be persisted to disk. **Durability with Chronicle
+ Map is provided by another level of architecture,** for example all requests are sent to several
+ nodes - master and hot standby. Clustering/distributed architecture is out of the scope of the
+ Chronicle Map project, there are projects *on top* of Chronicle Map which address these questions,
+ e. g. [Chronicle Enterprise](http://chronicle.software/products/chronicle-enterprise/).
+
+**What is the Chronicle Map data structure?** In one sentence and simplified, Chronicle Map instance
+is a big chunk of shared memory (optionally mapped to disk), split into independent segments, each
+segment has an independent memory allocation for storing the entries, a hash table for search, and a
+lock in shared memory (implemented via CAS loops) for managing concurrent access. Read [the
+Chronicle Map design overview](
+https://github.com/OpenHFT/Chronicle-Map/blob/master/spec/2-design-overview.md) for more.
+
+### Chronicle Map is *not*
+
+ - A document store. No secondary indexes.
+ - A [multimap](https://en.wikipedia.org/wiki/Multimap). Using a `ChronicleMap>`
+ as multimap is technically possible, but often leads to problems. Developing a proper multimap with
+ Chronicle Map's design principles is possible, [contact us](mailto:sales@chronicle.software) is
+ you consider sponsoring such development.
+
+Chronicle Map doesn't support
+
+ - Range queries, iteration over the entries in alphabetical order. Keys in Chronicle Map are not
+ sorted.
+ - LRU entry eviction
+
+Not supported out of the box in open-source version, but could be added in ad-hoc manner:
+ - Partially-redundant replication, i. e. distributed/cluster solution on top of Chronicle Map
+ - Synchronous replication
+ - Entry expiration timeouts
+
+[We could help to implement such things](http://chronicle.software/consultancy/).
+
+### Peer projects
+ - [Chronicle Engine](https://github.com/OpenHFT/Chronicle-Engine) - reactive processing framework
+ supporting Chronicle Map as a backend.
+ - [Chronicle Enterprise](http://chronicle.software/products/chronicle-enterprise/) - extended
+ version of Chronicle Engine.
+ - [Chronicle Journal](
+ http://vanillajava.blogspot.com/2015/09/chronicle-journal-customizable-data.html) - another
+ key-value built by Chronicle Software, with different properties.
+
+
+## Chronicle Map 3 Tutorial
#### Contents
-* [Overview](https://github.com/OpenHFT/Chronicle-Map#overview)
-* [Should I use Chronicle Queue or Chronicle Map](https://github.com/OpenHFT/Chronicle-Map#should-i-use-chroniclequeue-or-chroniclemap)
-* [What is the difference between SharedHashMap and Chronicle Map](https://github.com/OpenHFT/Chronicle-Map#what-is-the-difference-between-sharedhashmap-and-chroniclemap)
-* [JavaDoc](http://openhft.github.io/Chronicle-Map/apidocs)
-* [Getting Started Guide](https://github.com/OpenHFT/Chronicle-Map#getting-started)
- * [Simple Construction](https://github.com/OpenHFT/Chronicle-Map#simple-construction)
- * [Maven Download](https://github.com/OpenHFT/Chronicle-Map#maven-artifact-download-1)
- * [Snapshot Download](https://github.com/OpenHFT/Chronicle-Map#maven-snapshot-download)
- * [Key Value Object Types](https://github.com/OpenHFT/Chronicle-Map#key-value-object-types)
- * [Off Heap and How to improve performance](https://github.com/OpenHFT/Chronicle-Map#off-heap-storage-and-how-using-a-proxy-object-can-improve-performance)
- * [Sharing Data Between Two or More Maps](https://github.com/OpenHFT/Chronicle-Map#sharing-data-between-two-or-more-maps)
- * [Entries](https://github.com/OpenHFT/Chronicle-Map#entries)
- * [Size of Space Reserved on Disk](https://github.com/OpenHFT/Chronicle-Map#size-of-space-reserved-on-disk)
- * [Chronicle Map Interface](https://github.com/OpenHFT/Chronicle-Map#chronicle-map-interface)
-* [Serialization](https://github.com/OpenHFT/Chronicle-Map#serialization)
- * [Simple Types](https://github.com/OpenHFT/Chronicle-Map#simple-types)
- * [Complex Types](https://github.com/OpenHFT/Chronicle-Map#complex-types)
- * [Import/Export entries as JSON](https://github.com/OpenHFT/Chronicle-Map#importexport-entries-as-json)
-* [Close](https://github.com/OpenHFT/Chronicle-Map#close)
-* [TCP / UDP Replication](https://github.com/OpenHFT/Chronicle-Map#tcp--udp-replication)
- * [TCP / UDP Background.](https://github.com/OpenHFT/Chronicle-Map#tcp--udp-background)
- * [How to setup UDP Replication](https://github.com/OpenHFT/Chronicle-Map#how-to-setup-udp-replication)
- * [TCP/IP Throttling](https://github.com/OpenHFT/Chronicle-Map#tcpip--throttling)
- * [Replication How it works](https://github.com/OpenHFT/Chronicle-Map#replication-how-it-works)
- * [Multiple Processes on the same server with Replication](https://github.com/OpenHFT/Chronicle-Map#multiple-processes-on-the-same-server-with-replication)
- * [Identifier for Replication](https://github.com/OpenHFT/Chronicle-Map#identifier-for-replication)
- * [Bootstrapping](https://github.com/OpenHFT/Chronicle-Map#bootstrapping)
- * [Identifier](https://github.com/OpenHFT/Chronicle-Map#identifier)
- * [Port](https://github.com/OpenHFT/Chronicle-Map#port)
- * [Heart Beat Interval](https://github.com/OpenHFT/Chronicle-Map#heart-beat-interval)
-* [Multi Chronicle Maps - Network Distributed](https://github.com/OpenHFT/Chronicle-Map#multiple-chronicle-maps---network-distributed)
-* [Stateless Client](https://github.com/OpenHFT/Chronicle-Map#stateless-client)
-* [How to speed up the Chronicle Map Stateless Client](https://github.com/OpenHFT/Chronicle-Map#how-to-speed-up-the-chronicle-map-stateless-client)
-* [Questions/Answers](https://github.com/OpenHFT/Chronicle-Map#questions-and-answers)
+ - [Difference between Chronicle Map 2 and 3](#difference-between-chronicle-map-2-and-3)
+ - [Download the library](#download-the-library)
+ - [Create a `ChronicleMap` Instance](#create-a-chroniclemap-instance)
+ - [Key and Value Types](#key-and-value-types)
+ - [`ChronicleMap` instance usage patterns](#chroniclemap-instance-usage-patterns)
+ - [Single-key queries](#single-key-queries)
+ - [Multi-key queries](#multi-key-queries)
+ - [Close `ChronicleMap`](#close-chroniclemap)
+ - [TCP / UDP Replication](#tcp--udp-replication)
+ - [Multiple Processes on the same server with Replication](
+ #multiple-processes-on-the-same-server-with-replication)
+ - [Configuring Three Way TCP/IP Replication](#configuring-three-way-tcp--ip-replication)
+ - [Multi Chronicle Maps - Network Distributed](#multiple-chronicle-maps---network-distributed)
+ - [Behaviour Customization](#behaviour-customization)
+ - [Example - Simple logging](#example---simple-logging)
+ - [Example - BiMap](#example---bimap)
+ - [Example - CRDT values for replicated Chronicle Maps - Grow-only set](
+ #example---crdt-values-for-replicated-chronicle-maps---grow-only-set)
-#### Miscellaneous
+### Difference between Chronicle Map 2 and 3
- * [Known Issues](https://github.com/OpenHFT/Chronicle-Map#known-issues)
- * [Stackoverflow](http://stackoverflow.com/tags/chronicle/info)
- * [Development Tasks - JIRA] (https://higherfrequencytrading.atlassian.net/browse/HCOLL)
- * [Use Case Which include Chronicle Map] (http://chronicle.software/products/chronicle-engine/)
-
-#### Examples
+Changes in Chronicle Map 3:
- * [Hello World - A map which stores data off heap](https://github.com/OpenHFT/Chronicle-Map/blob/master/README.md#example--simple-hello-world)
- * [Sharing the map between two ( or more ) processes on the same computer](https://github.com/OpenHFT/Chronicle-Map/blob/master/README.md#example--sharing-the-map-on-two--or-more--processes-on-the-same-machine)
- * [Replicating data between process on different servers with TCP/IP Replication](https://github.com/OpenHFT/Chronicle-Map/blob/master/README.md#example--replicating-data-between-process-on-different-servers-via-tcp)
- * [Replicating data between process on different servers with UDP] (https://github.com/OpenHFT/Chronicle-Map/blob/master/README.md#example--replicating-data-between-process-on-different-servers-using-udp)
- * [Creating a Chronicle Set and adding data to it](https://github.com/OpenHFT/Chronicle-Map/blob/master/README.md#example--creating-a-chronicle-set-and-adding-data-to-it)
+ - Added support for multi-key queries.
+ - "Listeners" mechanism fully reworked, see the [Behaviour Customization](#behaviour-customization)
+ section. This has a number of important consequences, most notable is:
+ - Possibility to define replication eventual-consistency strategy, different from "last write
+ wins", e. g. any state-based CRDT.
+ - "Stateless clients" removed, access Chronicle Maps from remote servers via [Chronicle Engine](
+ https://github.com/OpenHFT/Chronicle-Engine).
+ - Chronicle Map 2 has hard creation-time limit on the number of entries storable in the Chronicle
+ Map instance. If the size exceeds this limit, an exception is thrown. In Chronicle Map 3, this
+ limitation is removed, though the number of entries still has to be configured on the Chronicle Map
+ instance creation, exceeding this configured limit is possible, but discouraged. See the
+ [Number of entries configuration](#number-of-entries-configuration) section.
+ - A number of smaller improvements and fixes.
-#### Performance Topics
+If you use Chronicle Map 2, you might be looking for [Chronicle Map 2 Tutorial](
+https://github.com/OpenHFT/Chronicle-Map/tree/2.1#contents) or [Chronicle Map 2 Javadoc](
+http://openhft.github.io/Chronicle-Map/apidocs/).
-* [Chronicle Map with Large Data ](https://github.com/OpenHFT/Chronicle-Map#chronicle-map-with-large-data)
-* [Lock Contention] (https://github.com/OpenHFT/Chronicle-Map/blob/master/README.md#lock-contention)
-* [Better to use small keys](https://github.com/OpenHFT/Chronicle-Map#better-to-use-small-keys)
-* [ConcurrentHashMap v ChronicleMap](https://github.com/OpenHFT/Chronicle-Map#concurrenthashmap-v-chroniclemap)
-
-### Overview
-Chronicle Map implements the interface `java.util.concurrent.ConcurrentMap`, however unlike the standard
-jdk implementations, ChronicleMap is both persistent and able to share your entries across processes:
-
-
-
-## When to use HashMap, ConcurrentHashMap and ChronicleMap
-#### When to use HashMap
-If you compare `HashMap`, `ConcurrentHashMap` and `ChronicleMap`, most of the maps in your system
-are likely to be HashMap. This is because `HashMap` is lightweight. Synchronized HashMap works
-well for lightly contended use cases. By contention we mean, how many threads on average are trying
-to use a Map. One reason you can't have many contended resources, is that you only have so many
-CPUs and they can only be accessing a limited resources at once (ideally no more than one or two
-per thread at a time).
-
-#### When to use ConcurrentHashMap
-`ConcurrentHashMap` scales very well when highly contended. It uses more memory but if you only
-have a few of them, this doesn't matter. They have higher throughput than the other two solutions,
-but it does create the highest amount of garbage. If garbage pressure is an issue for you (for example in
-a low latency evironment), you should consider `ChronicleMap`.
-
-#### When to use ChronicleMap
-If you have;
-* lots of small key-values
-* you want to minimise garbage produced, and medium lived objects.
-* you need to share data between JVMs
-* you need persistence
-
-#### Should I use ChronicleQueue or ChronicleMap
-ChronicleQueue is 'lossless', designed to send every update. If your network can't do this something has
-to give. You could compress the data but at some point you have to work within the limits of your
-hardware or get more hardware. ChronicleMap on the other hand sends the latest value only.
-This will naturally drop updates and is a more natural choice for low bandwidth connections.
-
-#### What is the difference between [SharedHashMap](https://github.com/OpenHFT/HugeCollections) and ChronicleMap
-SharedHashMap was the old name of what is now ChronicleMap. Since the last release of SharedHashMap
- we have added a lot of new features to ChronicleMap, most of these are listed in this readme.
-
-## Getting Started
-
-#### Tutorial 1 - Creating an instance of Chronicle Map
-[](http://chronicle.software/chronicle-map-video-tutorial-1/)
-
-### Simple Construction
-
-To download the JAR which contains Chronicle Map, we recommend you use maven, which will download it
-from [Maven Central](http://search.maven.org), once you have installed maven, all you have to do is
-add the following to your projects `pom.xml`:
-
-
+### Download the library
+#### Maven Artifact Download
```xml
net.openhft
@@ -135,20 +160,16 @@ add the following to your projects `pom.xml`:
```
-To get the latest version number
-[Click Here](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.openhft%22%20AND%20a%3A%22chronicle-map%22)
-
-when you add ( the above dependency ) to your pom maven will usually attempt to download the release artifacts from:
-```
-http://repo1.maven.org/maven2/net/openhft/chronicle-map
-```
+Click here to get the [Latest Version Number](
+http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.openhft%22%20AND%20a%3A%22chronicle-map%22)
#### Maven Snapshot Download
-If you want to try out the latest pre-release code, you can download the snapshot artifact manually from:
+If you want to try out the latest pre-release code, you can download the snapshot artifact manually
+from:
```xml
https://oss.sonatype.org/content/repositories/snapshots/net/openhft/chronicle-map/
```
-a better way is to add the following to your setting.xml, to allow maven to download snapshots :
+a better way is to add the following to your setting.xml, to allow maven to download snapshots:
```xml
@@ -165,559 +186,431 @@ and define the snapshot version in your pom.xml, for example:
net.openhft
chronicle-map
-
+
```
-#### Key Value Object Types
-
-Unlike HashMap which will support any heap object, ChronicleMap only works with objects that it
-can store off heap, so the objects have to be : (one of the following )
-
-- AutoBoxed primitives - for good performance.
-- Strings - for good performance.
-- implements Serializable
-- implements Externalizable ( with a public default constructor )
-- implements our custom interface BytesMarshallable ( with a public default constructor ) - use
-this for best performance.
-
-or value objects that are created through, a directClass interface, for example :
-``` java
- ChronicleMap chm = ChronicleMapBuilder
- .of(String.class, BondVOInterface.class)
- .create();
-
-```
-
-Object graphs can also be included as long as the outer object supports Serializable, Externalizable or BytesMarshallable.
-
-#### ChronicleMap Construction
+### Create a `ChronicleMap` Instance
Creating an instance of ChronicleMap is a little more complex than just calling a constructor.
-To create an instance you have to use the ChronicleMapBuilder.
+To create an instance you have to use the `ChronicleMapBuilder`.
``` java
import net.openhft.chronicle.map.*
.....
-try {
+interface PostalCodeRange {
+ int minCode();
+ void minCode(int minCode);
- String tmp = System.getProperty("java.io.tmpdir");
- String pathname = tmp + "/shm-test/myfile.dat";
-
- File file = new File(pathname);
-
- ChronicleMapBuilder builder =
- ChronicleMapBuilder.of(Integer.class, CharSequence.class);
- ConcurrentMap map = builder.createPersistedTo(file);
-
-} catch (IOException e) {
- e.printStackTrace();
+ int maxCode();
+ void maxCode(int maxCode);
}
-```
-ChronicleMap stores its data off the java heap, If you wish to share this off-heap memory between
-processes on the same server, you must provide a "file", this file must be the same "file" for all
-the instances of Chronicle Map on the same server. The name and location of the "file" is entirely
-up to you. For the best performance on many unix systems we recommend using
-[tmpfs](http://en.wikipedia.org/wiki/Tmpfs).
+ChronicleMapBuilder cityPostalCodesMapBuilder =
+ ChronicleMapBuilder.of(CharSequence.class, PostalCodeRange.class)
+ .averageKey("Amsterdam")
+ .entries(50_000);
+ChronicleMap cityPostalCodes =
+ cityPostalCodesMapBuilder.create();
-### Sharing Data Between Two or More Maps
-Since this file is memory mapped, if you were to create another instance of the ChronicleMap,
-pointing to the same file, both ChronicleMaps use this file as a common memory store, into which they
-both read and write. The good thing about this, is that the two ( or more instances of the ChronicleMap )
-don't have to be running in the same Java process. Ideally and for best performance, the two processes
-should be running on the same server. Since the file is memory mapped, ( in most cases ) the read
-and writes to the file are hitting the disk cache. This allows the ChronicleMap to exchange data
-between processes by just using memory and in around 40 nanoseconds.
+// Or shorter form, without builder variable extraction:
-``` java
-ConcurrentMap map1, map2;
-
-// this could be on one process
-map1 = ChronicleMapBuilder.of(Integer.class, CharSequence.class).createPersistedTo(file);
-
-// this could be on the other process
-map2 = ChronicleMapBuilder.of(Integer.class, CharSequence.class).createPersistedTo(file);
-```
-Note: In order to share data between map1 and map2, the file has to point to the same file location
-on your server.
-
-### Entries
-
-One of the differences with ChronicleMap against ConcurrentHashMap, is that it can't be resized.
-Unlike the ConcurrentHashMap, ChronicleMap is not limited to the available on heap memory.
-Resizing is a very expensive operation for HashMaps as it can stall your application, this is why
-we don't do it. When you are building a ChronicleMap you can set the maximum number of entries that
-you are ever likely to support but it's fine to overestimate this number. This is because ChronicleMap is not
-limited to your available memory therefore, at worst, you will end up having a large file on disk.
-
-You set the maximum number of entries using the builder:
-
-``` java
-ConcurrentMap map =
- ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- constantValueSizeBySample("a long sample string"),
- .entries(1000) // set the max number of entries here
+ChronicleMap cityPostalCodes = ChronicleMap
+ .of(CharSequence.class, PostalCodeRange.class)
+ .averageKey("Amsterdam")
+ .entries(50_000)
.create();
```
-In this example above we have set 1000 entries.
+This snippet creates a `ChronicleMap`, supposed to store about 50 000 city name -> postal code
+mappings. It is accessible within a single Java process - the process it is created within. The
+data is accessible while the process is alive.
-We have optimised ChronicleMap so that you can have situations where you either don't use;
+Replace `.create()` calls with `.createPersistedTo(cityPostalCodesFile)`, if you want the Chronicle
+Map to either
-- All the entries for which you have allowed. This works best on Unix where the disk space and memory
-used reflect the number of actual entries, not the number for which you have allowed.
+ - Outlive the process it was created within, e. g. to support hot Java application redeploy
+ - Be accessible from multiple processes on the same server
+ - Persist the data to disk
-- All the space you allow for each entry (see below). This helps if you have entries which are multiple cache
-lines (128 bytes +). Only the lines you touch sit in your CPU cache and if you have multiple pages
-(8+ Kbytes) only the pages you touch use memory or disk. The CPU cache usage matters as it can be
-10000x smaller than main memory.
+The `cityPostalCodesFile` has to represent the same location on your server among all Java
+processes, wishing to access this Chronicle Map instance, e. g.
+`System.getProperty("java.io.tmpdir") + "/cityPostalCodes.dat"`.
-#### Entry size
+The name and location of the file is entirely up to you.
-The size of each entry depends on the type of the Keys and Values, as some types are larger than others. For example, if an entry uses Integers for both the keys and values, both the key and value each take exactly 4 bytes.
-There is also some other overhead which is internal to chronicle, such as its internal multi-map, which has an overhead of
-* 6 bytes per entry for segment size of < 64k,
-* 9 bytes per entry for segment size of < 16m but > 64k
-* 12 bytes per entry for segment size of > 16m entries per segment.
+When no processes access the file, it could be freely moved to another location in the system, and
+even to another server, even running different operating system, opened from another location and
+you will observe the same data.
-Also, if you create a replicated map, there is an additional 10 bytes per entry.
+If you don't need the Chronicle Map instance to survive the server restart, i. e. you don't need
+persistence to disk, only multi-process access, choose the file to be mounted on [tmpfs](
+http://en.wikipedia.org/wiki/Tmpfs), e. g. on Linux it is as easy as placing you file in `/dev/shm`
+directory.
-We suggest you don't configure size for constant-sized keys or values, instead you can use the
-builder methods .constantKeySizeBySample(sampleKey) and
-.constantValueSizeBySample(sampleValue). For common types like Integer we suggest you don't use
-these methods, for example ChronicleMap understands that Integer is 4 bytes long, Long is 8, etc.
+---
+**You *must* configure `.entries(entries)` -- the supposed `ChronicleMap` size.** Try to configure
+the `entries` so that the created Chronicle Map is going to serve about 99% requests being less or
+equal than this number of entries in size.
-### Size of space reserved on disk
+*You shouldn't put additional margin over the actual target number of entries.* This bad practice
+was popularized by `new HashMap(capacity)` and `new HashSet(capacity)` constructors, which accept
+capacity, that should be multiplied by load factor to obtain the actual maximum expected number of
+entries in the container. `ChronicleMap` and `ChronicleSet` don't have a notion of load factor.
-In Linux, if you looked at the size of the 'file', it will report the used entry size so if you
-have just added one entry, it will report the size of this entry. Windows will report
-the reserved size, as it reserves the disk space eagerly ( in fact windows also reserves the memory
-eagerly as well ) in other words, number-of-entries x entry-size.
+See `ChronicleMapBuilder#entries()` [Javadocs](http://www.javadoc.io/doc/net.openhft/chronicle-map/)
+for more.
-so on Linux, if you type
-```
-# It shows you the extents.
-ls -l
+---
-# It shows you how much is actually used.
-du
+**Once `ChronicleMap` instance is created, it's configurations are sealed and couldn't be changed
+though the `ChronicleMapBuilder` instance.**
+
+---
+
+**Single `ChronicleMap` instance per JVM.** If you want to access the Chronicle Map instance
+concurrently within the Java process, you should *not* create a separate `ChronicleMap` instance per
+thread. Within the JVM environment, `ChronicleMap` instance *is* a `ConcurrentMap`, and could be
+accessed concurrently the same way as e. g. `ConcurrentHashMap`.
+
+#### Key and Value Types
+
+Either key or value type of `ChronicleMap` could be:
+
+ - Boxed primitive: `Integer`, `Long`, `Double`, etc.
+ - `String` or `CharSequence`
+ - Array of Java primitives, e. g. `byte[]`, `char[]` or `int[]`
+ - Any type implementing `BytesMarshallable` from [Chronicle Bytes](
+ https://github.com/OpenHFT/Chronicle-Bytes)
+ - Any [value interface](https://github.com/OpenHFT/Chronicle-Values)
+ - Any Java type implementing `Serializable` or `Externalizable` interface
+ - *Any other type*, if `.keyMarshaller()` or `.valueMarshaller()` (for the key or value type
+ respectively) is additionally configured in the `ChronicleMapBuilder`.
+
+**Prefer [value interfaces](https://github.com/OpenHFT/Chronicle-Values).** They don't generate
+garbage and have close to zero serialization/deserialization costs. Prefer them even to boxed
+primitives, for example, try to use `net.openhft.chronicle.core.values.IntValue` instead of
+`Integer`.
+
+**Generally, you *must* hint the `ChronicleMapBuilder` with the average sizes of the keys and
+values,** which are going to be inserted into the `ChronicleMap`. This is needed to allocate the
+proper volume of the shared memory. Do this via `averageKey()` (preferred) or `averageKeySize()` and
+`averageValue()` or `averageValueSize()` respectively.
+
+See the example above: `averageKey("Amsterdam")` is called, because it is assumed that "Amsterdam"
+(9 bytes in UTF-8 encoding) is the average length for city names, some names are shorter (Tokyo,
+5 bytes), some names are longer (San Francisco, 13 bytes).
+
+Another example: if values in your `ChronicleMap` are adjacency lists of some social graph, where
+nodes are represented as `long` ids, and adjacency lists are `long[]` arrays. The average number of
+friends is 150. Configure the `ChronicleMap` as follows:
+```java
+Map socialGraph = ChronicleMap
+ .of(Long.class, long[].class)
+ .entries(1_000_000_000L)
+ .averageValue(new long[150])
+ .create();
```
-To illustrate this with an example - On Ubuntu we can create a 100 TB ChronicleMap. Both `top` and
-`ls -l` say the process virtual size / file size is 100 TB, however the resident memory via `du`
-says the size is 71 MB after adding 10000 entries. You can see the size actually used with du.
+**You could omit specifying key or value average sizes, if their types are boxed Java primitives or
+value interfaces.** They are constantly-sized and Chronicle Map knows about that.
-### How Operating Systems differ
-
-As a pure Java library, the same ChronicleMap Java byte code can be run on Windows, Linux and Mac OSX,
-however these operating systems work with memory mapped files differently. These differences effect how
-ChronicleMap is able to map memory to a file and hence this can impact the total number of entries that you are
-able to configure.
-
-- Windows allocates memory and disk eagerly. Windows will fail if more than 4 GB is allocated in a single memory
-mapping, ( calculated as 4GB = 2^20 * 4 KB pages). Windows doesn't fail when a memory mapped region is mapped, rather it will fail when it is used up. Eager memory allocation means you can't map more than free memory, but it should reduce jitter when you use it. In the future we may support multiple mappings to avoid this limitation, but there is no immediate plan to do so.
-- Linux allocates memory and disk lazily. Linux systems see a performance degradation at around 200% of main memory.
-- Mac OSX allocates memory lazily and disk eagerly.
-
-ChronicleMap allocates head room which is a waste on Windows (Linux's sparse allocation means the head room has little
-impact).
-
-##### For production
-- on Windows we recommend you use map sizes of less than 4 GB each, and less than 50% main memory in total.
-- on Linux we recommend you use small to large maps of less than double main memory. e.g. if you have a 128 GB server, we recommend you have less than 256 GB of maps on the server.
-- on Mac OSX, we have no specific recommendations.
-
-### ChronicleMap Interface
-The ChronicleMap interface adds a few methods above and beyond the standard ConcurrentMap.
-The ChronicleMapBuilder can also be used to return the ChronicleMap, see the example below :
-
-``` java
-ChronicleMap map =
- ChronicleMapBuilder.of(Integer.class, CharSequence.class).create();
-```
-One way to achieve good performance is to focus on unnecessary object creation, as this reduces
-the amount of work that has to be carried out by the Garbage Collector. As such ChronicleMap
-supports the following methods :
-
- - [`V getUsing(K key, V value);`](http://openhft.github.io/Chronicle-Map/apidocs/net/openhft/chronicle/map/ChronicleMap.html#getUsing-K-V-)
- - [`V acquireUsing(K key, V value);`](http://openhft.github.io/Chronicle-Map/apidocs/net/openhft/chronicle/map/ChronicleMap.html#acquireUsing-K-V-)
- - [`ReadContext getUsingLocked(@NotNull K key, @NotNull V usingValue);`] (https://github.com/OpenHFT/Chronicle-Map#off-heap-storage-and-how-using-a-proxy-object-can-improve-performance)
- - [`WriteContext acquireUsingLocked(@NotNull K key, @NotNull V usingValue);`] (https://github.com/OpenHFT/Chronicle-Map#acquireusinglocked)
-
-These methods let you provide the object to which the data will be written so that the value used is mutable. For example
-
-``` java
-CharSequence using = new StringBuilder();
-CharSequence myResult = map.getUsing("key", using);
-// at this point "using" and "myResult" will both point to the same object
-```
-
-The `map.getUsing()` method is similar to `map.get()`, but because Chronicle Map stores its data off
-heap, if you were to call get("key"), a new object would be created each time. map.getUsing() works
-by reusing the heap memory which was used by the original Object "using". This technique provides
-you with better control over your object creation.
-
-Exactly like `map.getUsing()`, `map.acquireUsing()` will give you back a reference to an value
-based on
-a key, but unlike `map.getUsing()` if there is no entry in the map for this key the entry
-will be added and the value return will we the same value which you provided. ( The section below
- covers both `map.getUsing()` and `map.acquireUsing()` in more detail )
-
-#### Off heap storage and how using a Proxy Object can improve performance
-
-ChronicleMap stores its data off heap. There are some distinct advantages in using off heap data storage
-
- * When the off heap data is backed by a memory mapped file, the entire map and its contents are automatically persisted. So if you have to restart your system, you won’t lose the content of your map.
- * Off heap data structures are not visited by the garbage collector. With on heap maps like HashMap the garbage collector has to scan your entire object graph ( in fact just the live objects ) to remove garbage. If you are able to keep your on heap footprint low, the garbage collector has a lot less work to do, this in turn will improve performance.
- * ChronicleMap and its data is serialised so it can be stored in off
-heap (shared) memory. When this memory is shared between processes on the same server,
-ChronicleMap is able to distribute entries between processes with extremely low overhead.
-This is because both processes are sharing the same off heap memory space. Additionaly, since your objects are already stored off heap ( as a series of bytes ), replicating entries over the network adds relatively little overhead.
-
-For more information on the benefit's of off heap memory, see our article on - [On
-heap vs off
-heap memory
-usage]
-(http://vanillajava.blogspot.co.uk/2014/12/on-heap-vs-off-heap-memory-usage.html)
-
-One of the downsides of an off heap map is that whenever you wish to get a value ( on heap ) from an entry
-which is off heap, for example calling :
-
- ``` java
-Value v = get(key)
-```
-
-that entry has to be deserialised onto the Java heap so that you can use its value just like any other Java object. So if you were to call get(key) ten times,
-
- ``` java
-for(int i=1;i<=10;i++) {
- Value v = get(key)
-}
-```
-this would create 10 separate instances of the value. This is because each time get() is called, the map has to
-first create a Object to store the result in and then deserialise the value stored in the off
-heap entry. Assuming you want to get the value back on heap so that you can use it like a normal Java
-value, deserialisation is unavoidable. Deserialisation has to occur every time,
-( since the value may have been changed by another thread ), but we don’t have to create the
-object each time. This is why we introduced the method `getUsing(key,using)`. By reducing the number of objects
-you create, the amount of work that the garbage collector has to carry out is reduced,
-this in turn may improve overall performance. So back to `getUsing(key,using)`, if you wish to reuse
-and existing object ( in this case the ‘using’ value ), you can instead call :
-
- ``` java
-Value using = new Value();
-
-for(int i=1;i<=10;i++) {
- Value bond = map.getUsing(key,using); // this won’t create a new value each time.
- assert using == bond; // this will always be the same instance and the Vaule type can not be immutable
-}
-```
-We can get a further performance improvement if we don't deserialize the whole object,
-but deserialize only the bytes that in which we are actually interested,
-
-Lets assume that we had the following interface :
-
-``` java
-public interface LongValue {
- long getValue();
- void setValue(long value);
-}
-```
-It is possible to use chronicle as an off heap proxy that can go directly to the memory location off heap and just get back the long that you require. This is especially useful if the value object has a lot of fields in it like the BondVOInterface
-``` java
-public interface BondVOInterface {
- ...
-
- long getIssueDate();
- void setIssueDate(long issueDate); /* time in millis */
-
- long getMaturityDate();
- void setMaturityDate(long maturityDate); /* time in millis */
-
- double getCoupon();
- void setCoupon(double coupon);
-
- String getSymbol();
- void setSymbol(@MaxSize(20) String symbol);
- ...
-}
-```
-If for example you want to just do the following
-
-``` java
-BondVOInterface bond = map.get(key);
-bond.getCoupon()
-
-```
-lets say that it is only the `coupon` field that we are interested in, then its better not to have to
-deserialise the whole object that implements the `BondVOInterface`. The `ChronicleMapBuilder` will look a the types of keys
- and values that you use, If the value type is a simple accessor/mutator interface that is exposing a non nested pojo, which uses simple types
- like `CharSequence` and primitives with corresponding get..() and
- set..() methods, Chronicle is able to generate off heap poxies so the whole object is not
- deserialized each
- time it is accessed, The off heap proxies are able to read
-and write into
-the off heap data structures directly, this reduced serialisation can give you a big performance boost.
-See the example below which demonstartes how you can work directly with off heap entries.
-
-``` java
-ChronicleMap map = ChronicleMapBuilder
- .of(String.class, BondVOInterface.class)
- .keySize(10)
+If the key or value type is constantly sized, or keys or values only of a certain size appear in
+your Chronicle Map domain, you should prefer to configure `constantKeySizeBySample()` or
+`averageValueSizeBySample()`, instead of `averageKey()` or `averageValue()`, for example:
+```java
+ChronicleSet uuids =
+ ChronicleSetBuilder.of(UUID.class)
+ // All UUIDs take 16 bytes.
+ .constantKeySizeBySample(UUID.randomUUID())
+ .entries(1_000_000)
.create();
```
-by default, builder assume that we are going to work directly with the off heap
-entries (`DataValueClasses.directClassFor(BondVOInterface.class)`).
-The value class, in our case `BondVOInterface.class` is an `interface` rather than a `class`, as before, we can
-use the `getUsing(key,using)` method, but this time we have to create the ‘using’ instance slightly
-differently. We call either the `map.newValueInstance()` or `map.newKeyInstance()` method.
+### `ChronicleMap` instance usage patterns
-``` java
-BondVOInterface using = map.newValueInstance();
-```
+#### Single-key queries
-the call to `getUsing(key,value)` is the same as we had in the earlier example
+First of all, `ChronicleMap` supports all operations from [`Map`](
+https://docs.oracle.com/javase/8/docs/api/java/util/Map.html): `get()`, `put()`, etc, including
+methods added in Java 8, like `compute()` and `merge()`, and [`ConcurrentMap`](
+https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html) interfaces:
+`putIfAbsent()`, `replace()`. All operations, including those which include "two steps", e. g.
+`compute()`, are correctly synchronized in terms of `ConcurrentMap` interface.
-``` java
-BondVOInterface using = map.newValueInstance();
-BondVOInterface bond = map.getUsing(key,using);
-assert using == bond; // this will always be the same instance
+This means, you could use `ChronicleMap` instance just like a `HashMap` or `ConcurrentHashMap`:
+
+```java
+PostalCodeRange amsterdamCodes = Values.newHeapInstance(PostalCodeRange.class);
+amsterdamCodes.minCode(1011);
+amsterdamCodes.maxCode(1183);
+cityPostalCodes.put("Amsterdam", amsterdamCodes);
+
+...
+
+PostalCodeRange amsterdamCodes = cityPostalCodes.get("Amsterdam");
```
-`getUsing(key,using)` won’t create any on heap objects, and it won’t deserialize the ‘bond’ entry from off heap to on
-heap, all it does is sets the bond as a proxy to the off heap memory, this proxy object was created by
-`map.newValueInstance()`, it allows us access to the fields of our
-entry, directly into the off heap storage.
+However, this approach often generates garbage, because the values should be deserialized from
+off-heap memory to on-heap, the new value object are allocated. There are several possibilities to
+reuse objects efficiently:
-As above, ideally you would reuse the `using` variable.
+##### Value interfaces instead of boxed primitives
- ``` java
-BondVOInterface using = map.newValueInstance();
+If you want to create a `ChronicleMap` where keys are `long` ids, use `LongValue` instead of `Long`
+key:
-for(int i=1;i<=10;i++) {
- BondVOInterface bond = map.getUsing(key,using); // this won’t create a new value each time.
- double coupon = bond.getCoupon()
- assert using == bond; // this will always be the same instance
+```java
+ChronicleMap orders = ChronicleMap
+ .of(LongValue.class, Order.class)
+ .entries(1_000_000)
+ .create();
+
+LongValue key = Values.newHeapInstance(LongValue.class);
+key.setValue(id);
+orders.put(key, order);
+
+...
+
+long[] orderIds = ...
+LongValue key = Values.newHeapInstance(LongValue.class);
+for (long id : orderIds) {
+ key.setValue(id);
+ Order order = orders.get(key);
+ // process the order...
}
```
-so when you call :
+##### `chronicleMap.getUsing()`
-``` java
-bond.getCoupon()
+Use `ChronicleMap#getUsing(K key, V using)` to reuse the value object. It works if:
+
+ - The value type is `CharSequence`, pass `StringBuilder` as the `using` argument. For example:
+ ```java
+ ChronicleMap names = ...
+ StringBuilder name = new StringBuilder();
+ for (long id : ids) {
+ key.setValue(id);
+ names.getUsing(key, name);
+ // process the name...
+ }
+ ```
+ In this case, calling `names.getUsing(key, name)` is equivalent to
+ ```java
+ name.setLength(0);
+ name.append(names.get(key));
+ ```
+ with the difference that it doesn't generate garbage.
+ - The value type is value interface, pass heap instance to read the data into it without new object
+ allocation:
+ ```java
+ ThreadLocal cachedPostalCodeRange =
+ ThreadLocal.withInitial(() -> Values.newHeapInstance(PostalCodeRange.class));
+
+ ...
+
+ PostalCodeRange range = cachedPostalCodeRange.get();
+ cityPostalCodes.getUsing(city, range);
+ // process the range...
+ ```
+ - If the value type implements `BytesMarshallable`, or `Externalizable`, `ChronicleMap` attempts to
+ reuse the given `using` object by deserializing the value into the given object.
+ - If custom marshaller is configured in the `ChronicleMapBuilder` via `.valueMarshaller()`,
+ `ChronicleMap` attempts to reuse the given object by calling `readUsing()` method from the
+ marshaller interface.
+
+If `ChronicleMap` fails to reuse the object in `getUsing()`, it makes no harm, it falls back to
+object creation, like in `get()` method. In particular, even `null` is allowed to be passed as
+`using` object. It allows "lazy" using object initialization pattern:
+```java
+// a field
+PostalCodeRange cachedRange = null;
+
+...
+
+// in a method
+cachedRange = cityPostalCodes.getUsing(city, cachedRange);
+// process the range...
```
+In this example, `cachedRange` is `null` initially, on the first `getUsing()` call the heap value
+is allocated, and saved in a `cachedRange` field for later reuse.
-Note: It is only the coupon that gets deserialized.
+*If the value type is a value interface, **don't** use flyweight implementation as `getUsing()`
+argument.* This is dangerous, because on reusing flyweight points to the `ChronicleMap` memory
+directly, but the access is not synchronized. At least you could read inconsistent value state,
+at most - corrupt the `ChronicleMap` memory.
-Just like any other ConcurrentMap, ChronicleMap uses segment locking, if you wish to obtain a read lock
-when calling getUsing(key,using) you can do this :
+For accessing the `ChronicleMap` value memory directly use the following technique:
-``` java
-BondVOInterface bond = map.getUsing(key,value);
-try (ReadContext, BondVOInterface> context = map.getUsingLocked(key,bond)) {
- long issueDate = bond.getIssueDate();
- String symbol = bond.getSymbol();
+##### Working with the entry within a context section
- // add your logic here ( the lock will ensure this bond can not be changed by another thread )
-
-} // the lock is released here because close() is called
-
-```
-
-To ensure that 'issueDate' and 'symbol' can be read atomically, these values
-must be read while the segment read lock is in place.
-
-When you call map.getUsingLocked(key,using) we return a ReadContext, the ReadContext extends
-AutoCloseable so will automatically unlock the segment when the try block is exited.
-
-If you wish not to use a try block you must manually release the segment lock by calling
-``` java
-context.close() // releases the lock
-```
-
-#### acquireUsing()
-Just like getUsing(), acquireUsing() will also recycle the value you pass it. The following
-code snippet is a pattern that you will often come across. `acquireUsing(key,value)` offers this
-functionality from a single method call, reducing the hash look-ups and making your code run slightly faster.
-
- ``` java
-V acquireUsing(key,value) {
- Lock l = ...; // the segment lock of the map
- l.lock();
- try {
- V v = map.getUsing(key,value)
- if (v == null) {
- map.put(key,value); // create a new entry
- return value;
- } else
- return v; // return the value of the existing entry
- } finally {
- l.unlock();
+```java
+try (ExternalMapQueryContext c =
+ cityPostalCodes.queryContext("Amsterdam")) {
+ MapEntry entry = c.entry();
+ if (entry != null) {
+ PostalCodeRange range = entry.value().get();
+ // Access the off-heap memory directly, by calling range
+ // object getters.
+ // This is very rewarding, when the value has a lot of fields
+ // and expensive to copy to heap all of them, when you need to access
+ // just a few fields.
+ } else {
+ // city not found..
}
}
```
+#### Multi-key queries
-
-#### acquireUsingLocked()
-
-If you are only accessing ChronicleMap from a single thread. If you are not doing replication
-and don't care about atomic reads. Then its simpler ( and faster ) to use `acquireUsing(key,
-value)` otherwise werecommend you use `acquireUsingLocked(key,value)` as this gives you atomicity.
-
-The acquireUsingLocked(key,value) method holds a segment write lock, as it will update or put a
-new entry into the map, this is unlike getUsingLocked
-(key,using) which only holds a segment read lock. Below is an example of how to use
-acquireUsingLocked(..,..).
-
-``` java
-BondVOInterface bond = ... // create your instance
-try (WriteContext, BondVOInterface> context = map.acquireUsingLocked("one", bond)) {
- assert bond == context.value();
-
- long issueDate = bond.getIssueDate();
- String symbol = bond.getSymbol();
-
- if (condition) // based on your own business logic
- context.removeEntry();
+In this example, consistent graph edge addition and removals are implemented via multi-key queries:
+```java
+public static boolean addEdge(
+ ChronicleMap> graph, int source, int target) {
+ if (source == target)
+ throw new IllegalArgumentException("loops are forbidden");
+ ExternalMapQueryContext, ?> sourceC = graph.queryContext(source);
+ ExternalMapQueryContext, ?> targetC = graph.queryContext(target);
+ // order for consistent lock acquisition => avoid dead lock
+ if (sourceC.segmentIndex() <= targetC.segmentIndex()) {
+ return innerAddEdge(source, sourceC, target, targetC);
+ } else {
+ return innerAddEdge(target, targetC, source, sourceC);
+ }
}
-```
-
-If after some business logic, in our example after reading the 'issueDate' and
-'symbol', you wish to remove the entry, its more efficient to use the 'context' directly to remove the entry. The
-'context' is already aware of the entries location in memory. So it will be quicker to call
-`context.removeEntry()` rather than `map.remove(key)`.
-
-
-#### Example of an off heap byte[7]
-
-You can also create off heap arrays, see the following example
-
-``` java
-import net.openhft.chronicle.map.ChronicleMap;
-import net.openhft.chronicle.map.ChronicleMapBuilder;
-import net.openhft.lang.model.DataValueClasses;
-import net.openhft.lang.model.constraints.MaxSize;
-import net.openhft.lang.values.ByteValue;
-import net.openhft.lang.values.LongValue;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class OffHeapByteArrayExampleTest {
-
- public static final char EXPECTED = 'b';
-
- interface ByteArray {
- void setByteValueAt(@MaxSize(7) int index, ByteValue value);
-
- ByteValue getByteValueAt(int index);
+private static boolean innerAddEdge(
+ int source, ExternalMapQueryContext, ?> sourceContext,
+ int target, ExternalMapQueryContext, ?> targetContext) {
+ try (ExternalMapQueryContext, ?> sc = sourceContext) {
+ try (ExternalMapQueryContext, ?> tc = targetContext) {
+ sc.updateLock().lock();
+ tc.updateLock().lock();
+ MapEntry> sEntry = sc.entry();
+ if (sEntry != null) {
+ MapEntry> tEntry = tc.entry();
+ if (tEntry != null) {
+ return addEdgeBothPresent(sc, sEntry, source, tc, tEntry, target);
+ } else {
+ addEdgePresentAbsent(sc, sEntry, source, tc, target);
+ return true;
+ }
+ } else {
+ MapEntry> tEntry = tc.entry();
+ if (tEntry != null) {
+ addEdgePresentAbsent(tc, tEntry, target, sc, source);
+ } else {
+ addEdgeBothAbsent(sc, source, tc, target);
+ }
+ return true;
+ }
+ }
}
+}
- private static ChronicleMap chm;
-
- @BeforeClass
- public static void beforeClass() {
- chm = ChronicleMapBuilder
- .of(LongValue.class, ByteArray.class)
- .entries(1000)
- .create();
+private static boolean addEdgeBothPresent(
+ MapQueryContext, ?> sc,
+ @NotNull MapEntry> sEntry, int source,
+ MapQueryContext, ?> tc,
+ @NotNull MapEntry> tEntry, int target) {
+ Set sNeighbours = sEntry.value().get();
+ if (sNeighbours.add(target)) {
+ Set tNeighbours = tEntry.value().get();
+ boolean added = tNeighbours.add(source);
+ assert added;
+ sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours));
+ tEntry.doReplaceValue(tc.wrapValueAsData(tNeighbours));
+ return true;
+ } else {
+ return false;
}
+}
- @AfterClass
- public static void afterClass() {
- if (chm != null)
- chm.close();
+private static void addEdgePresentAbsent(
+ MapQueryContext, ?> sc,
+ @NotNull MapEntry> sEntry, int source,
+ MapQueryContext, ?> tc, int target) {
+ Set sNeighbours = sEntry.value().get();
+ boolean added = sNeighbours.add(target);
+ assert added;
+ sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours));
+
+ addEdgeOneSide(tc, source);
+}
+
+private static void addEdgeBothAbsent(MapQueryContext, ?> sc, int source,
+ MapQueryContext, ?> tc, int target) {
+ addEdgeOneSide(sc, target);
+ addEdgeOneSide(tc, source);
+}
+
+private static void addEdgeOneSide(MapQueryContext, ?> tc, int source) {
+ Set tNeighbours = new HashSet<>();
+ tNeighbours.add(source);
+ MapAbsentEntry> tAbsentEntry = tc.absentEntry();
+ assert tAbsentEntry != null;
+ tAbsentEntry.doInsert(tc.wrapValueAsData(tNeighbours));
+}
+
+public static boolean removeEdge(
+ ChronicleMap> graph, int source, int target) {
+ ExternalMapQueryContext, ?> sourceC = graph.queryContext(source);
+ ExternalMapQueryContext, ?> targetC = graph.queryContext(target);
+ // order for consistent lock acquisition => avoid dead lock
+ if (sourceC.segmentIndex() <= targetC.segmentIndex()) {
+ return innerRemoveEdge(source, sourceC, target, targetC);
+ } else {
+ return innerRemoveEdge(target, targetC, source, sourceC);
}
+}
- @Test
- public void test() {
-
- // this objects will be reused
- ByteValue byteValue = DataValueClasses.newDirectInstance(ByteValue.class);
- ByteArray value = chm.newValueInstance();
- LongValue key = chm.newKeyInstance();
-
-
- key.setValue(1);
-
- // this is kind of like byteValue[1] = 'b'
- byteValue.setValue((byte) EXPECTED);
- value.setByteValueAt(1, byteValue);
-
- chm.put(key, value);
-
- // clear the value to prove it works
- byteValue.setValue((byte) 0);
- value.setByteValueAt(1, byteValue);
-
- chm.getUsing(key, value);
-
- Assert.assertEquals(0, value.getByteValueAt(2).getValue());
- Assert.assertEquals(0, value.getByteValueAt(3).getValue());
-
- byte actual = value.getByteValueAt(1).getValue();
- Assert.assertEquals(EXPECTED, actual);
+private static boolean innerRemoveEdge(
+ int source, ExternalMapQueryContext, ?> sourceContext,
+ int target, ExternalMapQueryContext, ?> targetContext) {
+ try (ExternalMapQueryContext, ?> sc = sourceContext) {
+ try (ExternalMapQueryContext, ?> tc = targetContext) {
+ sc.updateLock().lock();
+ MapEntry> sEntry = sc.entry();
+ if (sEntry == null)
+ return false;
+ Set sNeighbours = sEntry.value().get();
+ if (!sNeighbours.remove(target))
+ return false;
+ tc.updateLock().lock();
+ MapEntry> tEntry = tc.entry();
+ if (tEntry == null)
+ throw new IllegalStateException("target node should be present in the graph");
+ Set tNeighbours = tEntry.value().get();
+ if (!tNeighbours.remove(source))
+ throw new IllegalStateException("the target node have an edge to the source");
+ sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours));
+ tEntry.doReplaceValue(tc.wrapValueAsData(tNeighbours));
+ return true;
+ }
}
}
```
-## Serialization
+Usage:
+```java
+HashSet averageValue = new HashSet<>();
+for (int i = 0; i < AVERAGE_CONNECTIVITY; i++) {
+ averageValue.add(i);
+}
+ChronicleMap> graph = ChronicleMapBuilder
+ .of(Integer.class, (Class>) (Class) Set.class)
+ .entries(100)
+ .averageValue(averageValue)
+ .create();
-
-
-
-ChronicleMap stores your data into off heap memory, so when you give it a Key or Value, it will
-serialise these objects into bytes.
-
-### Simple Types
-If you are using simple auto boxed objects based on the primitive types, ChronicleMap will
-automatically handle the serialisation for you.
-
-### Complex Types
-For anything other than the standard object, the Objects either have to :
-* implement "java.io.Serializable" ( which we don't recommend as this can be slow )
-* we also support "java.io.Externalizable", we recommend this over Serializable as its usually faster.
-* or for the best performance implement net.openhft.lang.io.serialization.BytesMarshallable,
-an example of how to do this can be found at "IntValue$$Native"
-* alternatively, you could write a "Custom Marshaller", the custom marshaller can be implemented
-for a single type or a number of types.
-
-### Import/Export entries as JSON
-
-
-ChronicleMap supports importing and exporting all the entries into a JSON encoded file.
-
-``` java
-void getAll(File toFile) throws IOException;
-void putAll(File fromFile) throws IOException;
+addEdge(graph, 1, 2);
+removeEdge(graph, 1, 2);
```
-Only the entries of your map are exported, not the configuration of your map. So care
-must be taken to populate the data in to a map of the correct Key/Value type and with enough
-available entries. When importing data :
-
-* entries that are in the map but not in the JSON file will remain untouched.
-* entries that are in the map and in the JSON file will be updated.
-* entries that are not in the map but are in the JSON file will be added.
-
-In other words importing data into a ChronicleMap works like `map.putAll()`.
-
-When importing data, if you are also writing to the map at the same time, the last update will win.
-In other words a write lock is not held for the entire import process.
-Importing and exporting the map, is ideal if you wish to:
-* Bulk load data from one ChronicleMap into another.
-* migrate data between versions of ChronicleMap.
-
-
-## Close
+### Close `ChronicleMap`
Unlike ConcurrentHashMap, ChronicleMap stores its data off heap, often in a memory mapped file.
Its recommended that you call close() once you have finished working with a ChronicleMap.
@@ -737,13 +630,13 @@ If you call close() too early before you have finished working with the map, thi
your JVM to crash. Close MUST BE the last thing that you do with the map.
-# TCP / UDP Replication
+### TCP / UDP Replication
ChronicleMap supports both TCP and UDP replication

-### TCP / UDP Background.
+#### TCP / UDP Background.
TCP/IP is a reliable protocol, what this means is that unless you have a network failure or hardware
outage the data is guaranteed to arrive. TCP/IP provides point to point connectivity. So in effect
( over simplified ), if the message was sent to 100 hosts, the message would have to be sent
@@ -753,18 +646,18 @@ with UDP is that it's not a reliable protocol. This means, if the UDP message is
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
+#### How to setup UDP Replication
In reality 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 a throttled TCP replication, therefore if a host misses a message over UDP, they
will later pick it up via TCP/IP.
-### TCP/IP Throttling
+#### 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 ChronicleMap only broadcasts the latest
update of each entry.
-### Replication How it works
+#### Replication How it works
ChronicleMap provides multi master hash map replication. What this means, is that each remote
map, mirrors its changes over to another remote map, neither map is considered
@@ -781,7 +674,7 @@ words the nodes are only concurrent locally. Its worth realising that another no
exactly the same operation may return a different value. However reconciliation will ensure the maps
themselves become eventually consistent.
-### Reconciliation
+#### 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. The aim of multi master replication is
@@ -807,7 +700,7 @@ to the other. This dilemma is resolved by using a node identifier, the node iden
'byte' value that is assigned to each node. When the time stamps are the same the remote node with the
smaller identifier will be preferred.
-### Multiple Processes on the same server with Replication
+#### Multiple Processes on the same server with Replication
On a server if you have a number of Java processes and then within each Java process you create an instance of a ChronicleMap which binds to the same underline 'file', they exchange data via shared memory rather than TCP or UDP replication. So if a ChronicleMap which is not performing TCP Replication is updated, this update can be picked up by another ChronicleMap. This other ChronicleMap could be a TCP replicated ChronicleMap. In such an example the TCP replicated ChronicleMap would then push the update to the remote nodes.
@@ -838,13 +731,15 @@ The identifier is setup on the builder as follows.
```java
TcpTransportAndNetworkConfig tcpConfig = ...
map = ChronicleMapBuilder
- .of(Integer.class, CharSequence.class)
+ .of(IntValue.class, CharSequence.class)
+ .averageValue(averageValue)
+ .entries(1_000_000)
.replication(identifier, tcpConfig)
.create();
```
-### Bootstrapping
+#### Bootstrapping
When a node is connected over the network to an active grid of nodes. It must first receive any data
that it does not have from the other nodes. Eventually, all the nodes in the grid have to hold a
copy of exactly the same data. We refer to this initial data load phase as bootstrapping.
@@ -856,7 +751,7 @@ your network, we suggest you consider using a throttle TCP replication along wit
Bootstrapping is not used when the nodes are on the same server, so for this case, TCP replication
is not required.
-### Identifier
+#### Identifier
Each map is allocated a unique identifier
Server 1 has:
@@ -876,7 +771,7 @@ Server 3 has:
If you fail to allocate a unique identifier replication will not work correctly.
-### Port
+#### Port
Each map must be allocated a unique port, the port has to be unique per server, if the maps are
running on different hosts they could be allocated the same port, but in our example we allocated
them different ports, we allocated map1 port 8076 and map2 port 8077. Currently we don't support
@@ -898,7 +793,7 @@ TcpTransportAndNetworkConfig.of(8077, new InetSocketAddress("localhost", 8076))
even though data flows from map1 to map2 and map2 to map1 it doesn't matter which way you connected
this, in other words its a bidirectional connection.
-### Configuring Three Way TCP/IP Replication
+#### Configuring Three Way TCP/IP Replication

@@ -930,7 +825,7 @@ TcpTransportAndNetworkConfig tcpConfigServer3 = TcpTransportAndNetworkConfig
.of(serverPort3, inetSocketAddress1, inetSocketAddress2);
```
-### Heart Beat Interval
+#### Heart Beat Interval
We set a heartBeatInterval, in our example to 1 second
``` java
heartBeatInterval(1, SECONDS)
@@ -939,7 +834,7 @@ A heartbeat will only be send if no data is transmitted, if the maps are constan
no heartbeat message is sent. If a map does not receive either data of a heartbeat the connection
is dropped and re-established.
-# Multiple Chronicle Maps - Network Distributed
+### Multiple Chronicle Maps - Network Distributed

@@ -984,10 +879,12 @@ Attaching a `ReplicationChannel` to the map :
``` java
short channel = (short) 2;
-ChronicleMap map = ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .entries(1000)
- .instance().replicatedViaChannel(replicationHub.createChannel(channel))
- .create();
+ChronicleMap map = ChronicleMap
+ .of(IntValue.class, CharSequence.class)
+ .averageValue(averageValue)
+ .entries(1000)
+ .instance().replicatedViaChannel(replicationHub.createChannel(channel))
+ .create();
```
The Chronicle channel is use to identify which map is to be replicated to which other map on
@@ -1004,7 +901,7 @@ If you inadvertently got the chronicle channels around the wrong way, then chron
to replicate the wrong maps data. The chronicle channels don't have to be in order but they must be
unique for each map you have.
-### Channels and ReplicationChannel - Example
+#### Channels and ReplicationChannel - Example
``` java
@@ -1024,8 +921,10 @@ ChronicleMap favoriteComputerServer1, favoriteComput
// server 1 with identifier = 1
{
- ChronicleMapBuilder builder =
- ChronicleMapBuilder.of(CharSequence.class, CharSequence.class).entries(1000);
+ ChronicleMapBuilder builder = ChronicleMapBuilder
+ .of(CharSequence.class, CharSequence.class)
+ .averageKeySize(10).averageValueSize(10)
+ .entries(1000);
byte identifier = (byte) 1;
@@ -1057,8 +956,10 @@ ChronicleMap favoriteComputerServer1, favoriteComput
// server 2 with identifier = 2
{
- ChronicleMapBuilder builder =
- ChronicleMapBuilder.of(CharSequence.class, CharSequence.class).entries(1000);
+ ChronicleMapBuilder builder = ChronicleMapBuilder
+ .of(CharSequence.class, CharSequence.class)
+ .averageKeySize(10).averageValueSize(10)
+ .entries(1000);
byte identifier = (byte) 2;
@@ -1106,494 +1007,22 @@ favoriteComputerServer2.close();
favoriteColourServer2.close();
favoriteColourServer1.close();
-```
-
-# Stateless Client
-
-
-
-A stateless client is an instance of a `ChronicleMap` or a `ChronicleSet` that does not hold any
-data
- locally, all the Map or Set operations are delegated via a Remote Procedure Calls ( RPC ) to
- another `ChronicleMap` or `ChronicleSet` which we will refer to as the server. The server
- holds all your data, the server can not it’s self be a stateless client. Your stateless client must
- be connected to the server via TCP/IP.
-
- 
-
- The stateless client delegates all your method calls to
- the remote server. The stateless client operations will block, in other words the stateless
- client waits for the server to send a response before continuing to the next operation. The stateless
- client could be consider to be a ClientProxy to `ChronicleMap` or `ChronicleSet` running
- on another host.
-
- Below is an example of how to configure a stateless client.
-
-``` java
-final ChronicleMap serverMap;
-final ChronicleMap statelessMap;
-
-// server
-{
-
- ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .replication((byte) 2, TcpTransportAndNetworkConfig.of(8076))
- .create();
-
- serverMap.put(10, "EXAMPLE-10");
-}
-
-// stateless client
-{
- statelessMap = ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .statelessClient(new InetSocketAddress("localhost", 8076))
- .create();
-
- Assert.assertEquals("EXAMPLE-10", statelessMap.get(10));
- Assert.assertEquals(1, statelessMap.size());
-}
-
-serverMap.close();
-statelessMap.close();
```
-When used with a stateless client, each statefull server has to be configured with TCP
-replication, when you set up TCP replication you must define a port for the replication to
-run on, the port you choose is up to you, but you should pick a free port that is not currently
-being used by another application. In this example we choose the port 8076
+### Behaviour Customization
-``` java
-// sets the server to run on localhost:8076
-.replication((byte) 2, TcpTransportAndNetworkConfig.of(8076))
-```
-
-On the "stateless client" we connect to the server via TCP/IP on localhost:8076 :
+You could customize `ChronicleMap` behaviour on several levels:
-``` java
-.statelessClient(new InetSocketAddress("localhost", 8076))
-```
+ - `ChronicleMapBuilder.entryOperations()` define the "inner" listening level, all operations with
+ entries, either during ordinary map method calls, remote calls, replication or modifications during
+ iteration over the map, operate via this configured SPI.
-but in your example you should choose the host of the statefull server and the port you allocated
- it.
+ - `ChronicleMapBuilder.mapMethods()` is the higher-level of listening for local calls of Map
+ methods. Methods in `MapMethods` interface correspond to `Map` interface methods with the same
+ names, and define their implementations for `ChronicleMap`.
-``` java
-.statelessClient(new InetSocketAddress(, ))
-```
-
-the ".statelessClient(..)" returns an instance of `StatelessClientConfig`, which has only a few
-of its own configurations, such as the `create()` method, which can be used to create a new
-stateless client.
-For this example we ran both
-the client and the server on the same host ( hence the “localhost" setting ),
-but in a real scanario the stateless client will typically be on a different server than its
-statefull host. If you are aiming to create a stateless client and server on the same host, it's
-better not to do this, as the stateless client connects to the server via TCP/IP. It would be better to
-share the maps via memory as this will give you better performance ( read more about
-this at [Sharing Data Between Two or More Maps](https://github
-.com/OpenHFT/Chronicle-Map#sharing-data-between-two-or-more-maps).
-
-click [here](https://github.com/OpenHFT/Chronicle-Map#sharing-data-between-two-or-more-maps )
-
-### How to speed up the Chronicle Map Stateless Client
-
-When calling the stateless client, you will get better throughput if you invoke your requests from a
- number of threads, this is because by default when you make a method call to a `ChronicleMap`
- stateless client, your method call is wrapped into an event which is sent over TCP and processed
- by the server. Your stateless client will block until an acknowledgement has been received from the server that
-the event was processed.
-
-
-
-When you are calling methods that return a value like get() this
-blocking adds no additional overhead, because you have to wait for the return value anyway,
-In some cases you could get better performance if you don't have to wait for the acknowledgement, This maybe the case when you are calling the `put()` method, but the problem with this method is it returns the old value even though you may not use it.
-
-For in memory data-structures like a HashMap this isn’t a big problem. But in a distributed environment things are a bit more complicated. Blocking for an old value that you don’t require adds additional network overhead and additional processing overhead of serialising the old value on the server and deserialising it on the client
-
-So if you don’t require the old value and don’t wish to block until your `put()` has been received by the server, then you may wish to consider using the following configuration :
-
-``` java
-.putReturnsNull(true)
-```
-
-and also for the `remove()` method
-
-``` java
-.removeReturnsNull(true)
-```
-
-``` java
-statelessClientMap = ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .putReturnsNull(true)
- .removeReturnsNull(true)
- .statelessClient(new InetSocketAddress("localhost", 8076))
- .create();
-```
-
-For the very best performance you should also set these properties on the server as well
-
-``` java
-ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .replication((byte) 2, TcpTransportAndNetworkConfig.of(8076))
- .putReturnsNull(true)
- .removeReturnsNull(true)
- .create();
-```
-
-##### Performance
-
-The throughput and latency performance for different configurations.
-
-Tested using a test called BGChronicleTest.
-
-On one machine (i7 3.5 GHz) we have two persisted replicas run as
-
--Dreplicas=2 eg.BGChronicleTest server
-
-On another machine (Dual Xeon 8 core 2.6 GHz) connected via a pair of Solarflare SFN5121T 10 Gig-E with onload enabled.
-
--Dreplicas=2 -Dclients={see below} -DmaxRate=30000 -DreadRatio=2 eg.BGChronicleTest client
-
-2 clients
-Throughput test
-messages per seconds: 58,864
-
-Latency test at 30,000 msg/sec
-50% / 90% / 99% // 99.9% / 99.99% / worst latency was 33 / 80 / 111 // 120 / 148 / 3,921 us
-
-4 clients
-Throughput test
-messages per seconds: 94,006
-
-Latency test at 30,000 msg/sec
-50% / 90% / 99% // 99.9% / 99.99% / worst latency was 32 / 94 / 106 // 131 / 177 / 2,153 us
-
-8 clients
-Throughput test
-messages per seconds: 162,961
-
-Latency test at 30,000 msg/sec
-50% / 90% / 99% // 99.9% / 99.99% / worst latency was 35 / 94 / 117 // 140 / 167 / 1,685 us
-
-16 clients
-Throughput test
-messages per seconds: 267,097
-
-Latency test at 30,000 msg/sec
-50% / 90% / 99% // 99.9% / 99.99% / worst latency was 38 / 97 / 122 // 149 / 174 / 2,771 us
-
-24 clients
-Throughput test
-messages per seconds: 253,052
-
-Latency test at 30,000 msg/sec
-50% / 90% / 99% // 99.9% / 99.99% / worst latency was 40 / 99 / 121 // 151 / 243 / 3,669 us
-
-##### Close
-
-its always important to close `ChronicleMap`'s and `ChronicleSet` 's when you have finished with them
-
-``` java
-serverMap.close();
-statelessMap.close();
-```
-
-# Known Issues
-
-##### Memory issue on Windows
-
-ChronicleMap lets you assign a map larger than your available memory, If you were to create more
-entries than the available memory, ChronicleMap will page the segments that are accessed least to
-disk, and load the recently used segments into available memory. This feature lets you work with
-extremely large maps, it works brilliantly on Linux but unfortunately, this paging feature is not
-supported on Windows, if you use more memory than is physically available on windows you will
-experience the following error :
-
-```java
-Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
-j sun.misc.Unsafe.compareAndSwapLong(Ljava/lang/Object;JJJ)Z+0
-j net.openhft.lang.io.NativeBytes.compareAndSwapLong(JJJ)Z+13
-j net.openhft.lang.io.AbstractBytes.tryLockNanos8a(JJ)Z+12
-j net.openhft.lang.io.AbstractBytes.tryLockNanosLong(JJ)Z+41
-j net.openhft.collections.AbstractVanillaSharedHashMap$Segment.lock()V+12
-```
-
-##### When Chronicle Map is Full
-
-`IllegalStateException` is thrown.
-
-ChronicleMap doesn't resize automatically. It is assumed you will make the virtual size of the map
-larger than you need and it will handle this reasonably efficiently. With the default settings you
-will run out of space between 1 and 2 million entries.
-
-You should set the `.entries(..)` and key/value size configurations to those you require.
-
-##### Don't forget to configure key and value sizes
-
-If your key type is not a boxed primitive or so-called "data value generated" interface,
-you should configure the key size in serialized form. If your key size varies,
-you should provide the average number of bytes of keys' serialized form,
-using method `averageKeySize()`:
-
-```java
-ChronicleMap wordFrequencies = ChronicleMapBuilder
- .of(String.class, LongValue.class)
- .entries(50000)
- .averageKeySize(6.5) // think 6.5 is the average number of chars in ASCII text,
- // taking the same number of bytes in UTF-8 encoding
- // (the default for Chronicle Map)
- .create();
-```
-
-If your key is constantly-sized, use `constantKeySizeBySample()`:
-
-```java
-ChronicleMap uuidToSomething = ChronicleMapBuilder
- .of(String.class, LongValue.class)
- .entries(50000)
- .constantKeySizeBySample(UUID.randomUUID().toString())
- .create();
-```
-
-The same applies to values, see javadocs to methods `ChronicleMapBuilder.averageValueSize()`
-and `ChronicleMapBuilder.constantValueSizeBySample()`.
-
-# Example : Simple Hello World
-
-This simple chronicle map, works just like ConcurrentHashMap but stores its data off-heap. If you
-want to use Chronicle Map to share data between java process you should look at the next exampl
-
-``` java
-Map map = ChronicleMapBuilder.of(Integer.class,
- CharSequence.class).create();
-
-map.put(1, "hello world");
-System.out.println(map.get(1));
-
-```
-
-# Example : Sharing the map on two ( or more ) processes on the same machine
-
-Lets assume that we had two server, lets call them server1 and server2, if we wished to share a map
-between them, this is how we could set it up
-
-``` java
-
-// --- RUN ON ONE JAVA PROCESS ( BUT ON THE SAME SERVER )
-{
- File file = new File("a-new-file-on-your-sever");
- Map map1 = ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .createPersistedTo(file); // this has to be the same file as used by map 2
- map1.put(1, "hello world");
-}
-
-// --- RUN ON THE OTHER JAVA PROCESS ( BUT ON THE SAME SERVER )
-{
- File file = new File("a-new-file-on-your-sever"); // this has to be the same file as used by map 1
- Map map1 = ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .createPersistedTo(file);
-
- System.out.println(map1.get(1));
-}
-```
-
-# Example : Replicating data between process on different servers via TCP/IP
-
-Lets assume that we had two server, lets call them server1 and server2, if we wished to share a map
-between them, this is how we could set it up
-
-``` java
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-public class YourClass {
-
- @Test
- public void test() throws IOException, InterruptedException {
-
- Map map1;
- Map map2;
-
-// ---------- SERVER1 1 ----------
- {
-
- // we connect the maps via a TCP/IP socket connection on port 8077
-
- TcpTransportAndNetworkConfig tcpConfig = TcpTransportAndNetworkConfig
- .of(8076, new InetSocketAddress("localhost", 8077))
- .heartBeatInterval(1L, TimeUnit.SECONDS);
- ChronicleMapBuilder map1Builder =
- ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .entries(20000L)
- .replication((byte) 1, tcpConfig);
-
- map1 = map1Builder.create();
- }
-// ---------- SERVER2 on the same server as ----------
-
- {
- TcpTransportAndNetworkConfig tcpConfig =
- TcpTransportAndNetworkConfig.of(8077)
- .heartBeatInterval(1L, TimeUnit.SECONDS);
- ChronicleMapBuilder map2Builder =
- ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .entries(20000L)
- .replication((byte) 2, tcpConfig);
- map2 = map2Builder.create();
-
- // we will stores some data into one map here
- map2.put(5, "EXAMPLE");
- }
-
-// ---------- CHECK ----------
-
-// we are now going to check that the two maps contain the same data
-
-// allow time for the recompilation to resolve
- int t = 0;
- for (; t < 5000; t++) {
- if (map1.equals(map2))
- break;
- Thread.sleep(1);
- }
-
- Assert.assertEquals(map1, map2);
- Assert.assertTrue(!map1.isEmpty());
- }
-
-}
-```
-
-# Example : Replicating data between process on different servers using UDP
-
-This example is the same as the one above, but it uses a slow throttled TCP/IP connection to fill in
-updates that may have been missed when sent over UDP. Usually on a good network, for example a wired
-LAN, UDP won’t miss updates. But UDP does not support guaranteed delivery, we recommend also running
-a TCP connection along side to ensure the data becomes eventually consistent. Note : It is possible
-to use Chronicle without the TCP replication and just use UDP ( that’s if you like living dangerously ! )
-
-``` java
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.InetSocketAddress;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-public class YourClass {
-
- @Test
- public void test() throws IOException, InterruptedException {
-
- Map map1;
- Map map2;
-
- int udpPort = 1234;
-
-// ---------- SERVER1 1 ----------
- {
-
- // we connect the maps via a TCP socket connection on port 8077
-
- TcpTransportAndNetworkConfig tcpConfig = TcpTransportAndNetworkConfig
- .of(8076, new InetSocketAddress("localhost", 8077))
- .heartBeatInterval(1L, TimeUnit.SECONDS)
-
- // a maximum of 1024 bits per millisecond
- .throttlingConfig(ThrottlingConfig.throttle(1024, TimeUnit.MILLISECONDS));
-
- UdpTransportConfig udpConfig = UdpTransportConfig
- .of(Inet4Address.getByName("255.255.255.255"), udpPort);
-
- ChronicleMapBuilder map1Builder =
- ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .entries(20000L)
- .replication(SingleChronicleHashReplication.builder()
- .tcpTransportAndNetwork(tcpConfig)
- .udpTransport(udpConfig)
- .createWithId((byte) 1));
-
- map1 = map1Builder.create();
- }
-// ---------- SERVER2 2 on the same server as ----------
-
- {
- TcpTransportAndNetworkConfig tcpConfig =
- TcpTransportAndNetworkConfig.of(8077)
- .heartBeatInterval(1L, TimeUnit.SECONDS)
- .throttlingConfig(ThrottlingConfig.throttle(1024, TimeUnit.MILLISECONDS));
-
- UdpTransportConfig udpConfig = UdpTransportConfig
- .of(Inet4Address.getByName("255.255.255.255"), udpPort);
-
- ChronicleMapBuilder map2Builder =
- ChronicleMapBuilder.of(Integer.class, CharSequence.class)
- .entries(20000L)
- .replication(SingleChronicleHashReplication.builder()
- .tcpTransportAndNetwork(tcpConfig)
- .udpTransport(udpConfig)
- .createWithId((byte) 2));
-
- map2 = map2Builder.create();
-
- // we will stores some data into one map here
- map2.put(5, "EXAMPLE");
- }
-
-// ---------- CHECK ----------
-
-// we are now going to check that the two maps contain the same data
-
-// allow time for the recompilation to resolve
- int t = 0;
- for (; t < 5000; t++) {
- if (map1.equals(map2))
- break;
- Thread.sleep(1);
- }
-
- Assert.assertEquals(map1, map2);
- Assert.assertTrue(!map1.isEmpty());
- }
-}
-```
-
-# Example : Creating a Chronicle Set and adding data to it
-
-This project also provides the Chronicle Set, `ChronicleSet` is built on Chronicle Map, so the builder
-configuration are almost identical to `ChronicleMap` ( see above ), this example shows how to create
-a simple off heap set
-``` java
- Set set = ChronicleSetBuilder.of(Integer.class).create();
-
- set.add(1);
- set.remove(1)
-```
-and just like map it support shared memory and TCP replication.
-
-# Customizing Chronicle Map behaviour and using listeners
-
-You could customize Chronicle Map behaviour on several levels:
-
- - [`ChronicleMapBuilder.entryOperations()`](http://openhft.github.io/Chronicle-Map/apidocs/net/openhft/chronicle/map/ChronicleMapBuilder.html#entryOperations)
- define the "inner" listening level, all operations with entries, either during ordinary map
- method calls, remote calls, replication or modifications during iteration over the map, operate
- via this configured SPI.
-
- - [`ChronicleMapBuilder.mapMethods()`](http://openhft.github.io/Chronicle-Map/apidocs/net/openhft/chronicle/map/ChronicleMapBuilder.html#mapMethods)
- is the higher-level of listening for local calls of Map methods. Methods in `MapMethods`
- interface correspond to `Map` interface methods with the same names, and define their
- implementations for `ChronicleMap`.
-
- - [`ChronicleMapBuilder.remoteOperations()`](http://openhft.github.io/Chronicle-Map/apidocs/net/openhft/chronicle/map/ChronicleMapBuilder.html#remoteOperations)
- is for listening and customizing behaviour of remote calls, and replication events.
+ - `ChronicleMapBuilder.remoteOperations()` is for listening and customizing behaviour of remote
+ calls, and replication events.
All executions around `ChronicleMap` go through the three tiers (or the two bottom):
@@ -1605,25 +1034,22 @@ All executions around `ChronicleMap` go through the three tiers (or the two bott
the Query tier. `MapEntryOperations` methods accept `MapEntry` or `MapAbsentEntry`, i. e. this SPI
is between Query and Entry tiers.
-Combined, listening/customization SPI interfaces and `ChronicleMap.queryContext()` API are powerful
-enough to
+Combined, interception SPI interfaces and `ChronicleMap.queryContext()` API are powerful enough to
- - Log all operations of some kind on ChronicleMap (e. g. all remove, insert or update operations)
- - Log some specific operations on ChronicleMap (e. g. log only acquireUsing() calls, which
- has created a new entry)
- - Forbid performing operations of some kind on the ChronicleMap instance
- - Backup all changes to ChronicleMap to some durable storage, e. g. SQL database
- - Perform multi-ChronicleMap operations correctly in concurrent environment,
- by acquiring locks on all ChronicleMaps before updating them.
- - Perform multi-key operations on a single ChronicleMap correctly in concurrent environment,
- by acquiring locks on all keys before updating the entries
- - Define own replication/reconciliation logic for distributed ChronicleMaps
+ - Log all operations of some kind on `ChronicleMap` (e. g. all remove, insert or update operations)
+ - Log some specific operations on `ChronicleMap` (e. g. log only acquireUsing() calls, which has
+ created a new entry)
+ - Forbid performing operations of some kind on the `ChronicleMap` instance
+ - Backup all changes to `ChronicleMap` to some durable storage, e. g. SQL database
+ - Perform multi-Chronicle Map operations correctly in concurrent environment, by acquiring locks on
+ all ChronicleMaps before updating them.
+ - Perform multi-key operations on a single `ChronicleMap` correctly in concurrent environment, by
+ acquiring locks on all keys before updating the entries
+ - Define own replication/reconciliation logic for distributed Chronicle Maps
-## Examples
+#### Example - Simple logging
-### Simple logging
-
-Just log all modification operations on ChronicleMap
+Just log all modification operations on `ChronicleMap`
```
class SimpleLoggingMapEntryOperations implements MapEntryOperations {
@@ -1657,20 +1083,13 @@ class SimpleLoggingMapEntryOperations implements MapEntryOperations defaultValue(@NotNull MapAbsentEntry absentEntry) {
- Data defaultValue = absentEntry.defaultValue();
- System.out.println("default " + absentEntry.absentKey() + " -> " + defaultValue);
- return defaultValue;
- }
}
```
Usage:
```
-ChronicleMap map = ChronicleMapBuilder
+ChronicleMap map = ChronicleMap
.of(Integer.class, IntValue.class)
.entries(100)
.entryOperations(simpleLoggingMapEntryOperations())
@@ -1679,7 +1098,7 @@ ChronicleMap map = ChronicleMapBuilder
// do anything with the map
```
-### BiMap
+#### Example - BiMap
Possible bidirectional map (i. e. a map that preserves the uniqueness of its values as well
as that of its keys) implementation over Chronicle Maps.
@@ -1901,7 +1320,7 @@ map1.put(1, "1");
System.out.println(map2.get("1"));
```
-### CRDT values for replicated Chronicle Maps -- Grow-only set
+#### Example - CRDT values for replicated Chronicle Maps - Grow-only set
`Set` values won't replace each other on replication, but will converge to a single, common set,
the union of all elements added to all sets on all replicated nodes.
@@ -1979,425 +1398,3 @@ ChronicleMap> map1 = ChronicleMapBuilder
.name("map1")
.create();
```
-
-### Undirected graph
-
-Using nested contexts:
-
-```java
-public static boolean addEdge(
- ChronicleMap> graph, int source, int target) {
- if (source == target)
- throw new IllegalArgumentException("loops are forbidden");
- ExternalMapQueryContext, ?> sourceC = graph.queryContext(source);
- ExternalMapQueryContext, ?> targetC = graph.queryContext(target);
- // order for consistent lock acquisition => avoid dead lock
- if (sourceC.segmentIndex() <= targetC.segmentIndex()) {
- return innerAddEdge(source, sourceC, target, targetC);
- } else {
- return innerAddEdge(target, targetC, source, sourceC);
- }
-}
-
-private static boolean innerAddEdge(
- int source, ExternalMapQueryContext, ?> sourceContext,
- int target, ExternalMapQueryContext, ?> targetContext) {
- try (ExternalMapQueryContext, ?> sc = sourceContext) {
- try (ExternalMapQueryContext, ?> tc = targetContext) {
- sc.updateLock().lock();
- tc.updateLock().lock();
- MapEntry> sEntry = sc.entry();
- if (sEntry != null) {
- MapEntry> tEntry = tc.entry();
- if (tEntry != null) {
- return addEdgeBothPresent(sc, sEntry, source, tc, tEntry, target);
- } else {
- addEdgePresentAbsent(sc, sEntry, source, tc, target);
- return true;
- }
- } else {
- MapEntry> tEntry = tc.entry();
- if (tEntry != null) {
- addEdgePresentAbsent(tc, tEntry, target, sc, source);
- } else {
- addEdgeBothAbsent(sc, source, tc, target);
- }
- return true;
- }
- }
- }
-}
-
-private static boolean addEdgeBothPresent(
- MapQueryContext, ?> sc,
- @NotNull MapEntry> sEntry, int source,
- MapQueryContext, ?> tc,
- @NotNull MapEntry> tEntry, int target) {
- Set sNeighbours = sEntry.value().get();
- if (sNeighbours.add(target)) {
- Set tNeighbours = tEntry.value().get();
- boolean added = tNeighbours.add(source);
- assert added;
- sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours));
- tEntry.doReplaceValue(tc.wrapValueAsData(tNeighbours));
- return true;
- } else {
- return false;
- }
-}
-
-private static void addEdgePresentAbsent(
- MapQueryContext, ?> sc,
- @NotNull MapEntry> sEntry, int source,
- MapQueryContext, ?> tc, int target) {
- Set sNeighbours = sEntry.value().get();
- boolean added = sNeighbours.add(target);
- assert added;
- sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours));
-
- addEdgeOneSide(tc, source);
-}
-
-private static void addEdgeBothAbsent(MapQueryContext, ?> sc, int source,
- MapQueryContext, ?> tc, int target) {
- addEdgeOneSide(sc, target);
- addEdgeOneSide(tc, source);
-}
-
-private static void addEdgeOneSide(MapQueryContext, ?> tc, int source) {
- Set tNeighbours = new HashSet<>();
- tNeighbours.add(source);
- MapAbsentEntry> tAbsentEntry = tc.absentEntry();
- assert tAbsentEntry != null;
- tAbsentEntry.doInsert(tc.wrapValueAsData(tNeighbours));
-}
-
-public static boolean removeEdge(
- ChronicleMap> graph, int source, int target) {
- ExternalMapQueryContext, ?> sourceC = graph.queryContext(source);
- ExternalMapQueryContext, ?> targetC = graph.queryContext(target);
- // order for consistent lock acquisition => avoid dead lock
- if (sourceC.segmentIndex() <= targetC.segmentIndex()) {
- return innerRemoveEdge(source, sourceC, target, targetC);
- } else {
- return innerRemoveEdge(target, targetC, source, sourceC);
- }
-}
-
-private static boolean innerRemoveEdge(
- int source, ExternalMapQueryContext, ?> sourceContext,
- int target, ExternalMapQueryContext, ?> targetContext) {
- try (ExternalMapQueryContext, ?> sc = sourceContext) {
- try (ExternalMapQueryContext, ?> tc = targetContext) {
- sc.updateLock().lock();
- MapEntry> sEntry = sc.entry();
- if (sEntry == null)
- return false;
- Set sNeighbours = sEntry.value().get();
- if (!sNeighbours.remove(target))
- return false;
-
- tc.updateLock().lock();
- MapEntry> tEntry = tc.entry();
- if (tEntry == null)
- throw new IllegalStateException("target node should be present in the graph");
- Set tNeighbours = tEntry.value().get();
- if (!tNeighbours.remove(source))
- throw new IllegalStateException("the target node have an edge to the source");
- sEntry.doReplaceValue(sc.wrapValueAsData(sNeighbours));
- tEntry.doReplaceValue(tc.wrapValueAsData(tNeighbours));
- return true;
- }
- }
-}
-```
-
-Usage:
-```java
-HashSet averageValue = new HashSet<>();
-for (int i = 0; i < AVERAGE_CONNECTIVITY; i++) {
- averageValue.add(i);
-}
-ChronicleMap> graph = ChronicleMapBuilder
- .of(Integer.class, (Class>) (Class) Set.class)
- .entries(100)
- .averageValue(averageValue)
- .create();
-
-addEdge(graph, 1, 2);
-removeEdge(graph, 1, 2);
-```
-
-# Performance Topics
-
-There are general principles we can give direction on - for specific advise we believe consulting
- to be the most productive solution.
-
-We want the Map to be reasonably general purpose, so in broad terms we can say
-- the key and values have to be self contained, ideally trees, rather than graphs.
-- ideally values are similar lengths, however we support varying lengths.
-- ideally you want to use primitives and use object recycling for performance, though this is not a requirement.
-- ideally you have some idea as to the maximum number of entries, though it is not too important if
-the maximum entries is above what you need.
-- if for example you are working with, market depth, this can be supported via an array of nested
-types.
-- we support code generation of efficient custom serializes - See the examples where you provide
-an interface as the data type, the map will generate the implementation.
-
-### Improving the performance of Chronicle Maps Serialization
-
-Are you finding that when using Chronicle Map the serialisation is slow, but you have a large object graph,
-if so -
-here are some steps that you can take to improve performance ( you may find that just one of these steps gives you the performance you require )
-
-* consider replacing java.io.Serializable with java.io.Externalizable ( java.io.Externalizable can be much
-faster, but requires you do the the serialisation your self)
-* consider using net.openhft.lang.io.serialization.BytesMarshallable, working with this is like using java
-.io.Externalizable but in addition is supports some compressed types, which can give you slightly better performance.
-* try where possible to de-normalize your object graph, if you are able to limit your graph down to just a
-few types you can then look at using our off heap interface proxy objects and have a map instance per type
-of object
- ( much like in a database where you would have a table per type ), you could then set up foreign key
- relationships between the maps, by using for example integers or longs. This works especially well as
- integers and longs have a good hash distribution.
-
-### Tuning Chronicle Map with Large Data
-
-Generally speaking `ChronicleMap` is slower then ConcurrentHashMap for a small number of entries, but
-for a large number of entries ConcurrentHashMap doesn't scale as well as Chronicle Map, especially
-when you start running low on heap. ConcurrentHashMap quickly becomes unusable whereas Chronicle Map
-can still work when it is 20 times the size of a ConcurrentHashMap with an Out of Memory Error.
-
-For example with a heap of 3/4 of say 32 GB main memory, you might get say 100 million entries but
-when using most of the heap you might see 20-40 second gc pauses with `ChronicleMap` you could have
-1000 million entries and see < 100 ms pauses (depending on your disk subsystem and how fast you
-write your data)
-
-Chronicle Map makes heavy use of the OS to perform the memory management and writing to disk. How it
-behaves is very dependant on how you tune the kernel and what hardware you are using. You may get
-bad behaviour when the kernel forces a very large amount of data to disk after letting a lot of
-uncommited data build up. In the worst case scenario the OS will stop the process for tens of
-seconds at a time ( even up to 40 seconds) rather than let the program continue. However, to get
-into that state you have to be loading a lot of data which exceeds main memory with very little rest
-(e.g. cpu processing). There are good use cases for bulk data loads, but you have to be careful how
-this is done if you also want good worst case latency characteristics. (the throughput should be
-much the same)
-
-When you create a ChronicleMap, it has many segments. By default it has a minimum of 128, but one
-for every 32 K entries. e.g. for 500M entries you can expect ~16K segments (being the next power of
-2). With so many segments, the chances of a perfect hash distribution is low and so the Chronicle
-Map allows for double what you asked for but is designed to do this with almost no extra main memory
-(only extra virtual memory). This means when you ask for 500M * 256 bytes entries you actually get 1
-BN possible entries (assuming a perfect hash distribution between segments) There is a small
-overhead per entry of 16 - 24 bytes adding another 20 GB.
-
-So while the virtual memory is 270 GB, it is expected that for 500 M entries you will be trying to
-use no more than 20 GB (overhead/hash tables) + ~120 GB (entries)
-
-When `ChronicleMap` has exhausted all the memory on your server, its not going to be so fast, for a
-random access pattern you are entirely dependant on how fast your underlying disk is. If your home
-directory is an HDD and its performance is around 125 IOPS (I/Os per second). Each lookup takes two
-memory accesses so you might get around 65 lookups per second. For 100-200K operations you can
-expect around 1600 seconds or 25-50 minutes. If you use an SSD, it can get around 230 K IOPS, or
-about 115 K `ChronicleMap` lookups per second.
-
-### Lock contention
-
-If you see the following warning :
-
-``` java
-WARNING:net.openhft.lang.io.AbstractBytes tryLockNanosLong0
-WARNING: Thread-2, to obtain a lock took 0.129 seconds
-```
-
-It's likely you have lock contention, this can be due to :
-
-- a low number of segments and
-- the machine was heavily over utilised, possibly with the working data set larger than main memory.
-- you have a large number of threads, greater than the number of cores you have, doing nothing but hit one map.
-
-It’s not possible to fully disable locking, locking is done a a segment basis.
-So, If you set a large number of actual segments, this will reduce your lock contention.
-
-See the example below to see how to set the number of segments :
-
-``` java
-ChronicleMap map = ChronicleMapBuilder.of(Long.class, String.class)
- .entries(100)
- .actualSegments(100) // set your number of segments here
- .create();
-```
-
-Reducing lock contention will make this warning message go away, but this message maybe more of a symptom
-of a general problem with what the system is doing, so you may experience a delay anyway.
-
-### Better to use small keys
-
-If you put() a small number of large entries into ChronicleMap, you are unlikely to see any
-performance gains over a standard map, So we recommend you use a standard ConcurrentHashMap, unless
-you need ChronicleMaps other features.
-
-Chronicle Map gives better performance for smaller keys and values due to the low overhead per
-entry. It can use 1/5th the memory of ConcurrentHashMap. When you have larger entries, the overhead
-per entry doesn't matter so much and the relative waste per entry starts to matter. For Example,
-ChronicleMap assumes every entry is the same size and if you have 10kB-20kB entries the 10K entries
-can be using 20 kB of virtual memory or at least 12 KB of actual memory (since virtual memory turns
-into physical memory in multiples of a page)
-
-As the `ChronicleMap` gets larger the most important factor is the use of CPU cache rather than main
-memory, performance is constrained by the number of cache lines you have to touch to update/read an
-entry. For large entries this is much the same as ConcurrentHashMap. In this case, `ChronicleMap` is
-not worse than ConcurrentHashMap but not much better.
-
-For large key/values it is not total memory use but other factors which matter such as;
-- how compact each entry is. Less memory used makes better use of the L3 cache and memory bus which
- is often a bottleneck in highly concurrent applications.
-- reduce the impact on GCs. The time to perform GC and its impact is linear. Moving the bulk of
- your data off heap can dramatically improve throughput not to mention worst case latency.
-- Large data structures take a long time to reload and having a persisted store significantly
- reduces restart times.
-- data can be shared between processes. This gives you more design options to share between JVMS and
- support short lived tasks without having to use TCP.
-- data can be replicated across machines.
-
-### ConcurrentHashMap v ChronicleMap
-ConcurrentHashMap ( CHM ) outperforms `ChronicleMap` ( CM ) on throughput. If you don't need
-the extra features ChronicleMap gives you, it is not worth the extra complexity it brings.
-i.e. don't use it just because you think it is cool. The test can be found in
-[ChronicleMapTest](https://github.com/OpenHFT/Chronicle-Map/blob/master/src/test/java/net/openhft/chronicle/map/ChronicleMapTest.java)
-under testAcquirePerf() and testCHMAcquirePerf()
-
-ChronicleMap outperforms ConcurrentHashMap on memory consumption, and worst case latencies.
-It can be used to reduce or eliminate GCs.
-
-#### Performance Test for many small key-values
-The following performance test consists of string keys of the form "u:0123456789" and an int
-counter. The update increments the counter once in each thread, creating an new entry if required.
-
-| Number of entries | Chronicle* Throughput | Chronicle RSS | HashMap* Throughput | HashMap Worst GC pause | HashMap RSS |
-|------------------:|---------------:|---------:|---------------:|-------------------:|--------:|
-| 10 million | 30 Mupd/s | ½ GB | 155 Mupd/s | 2.5 secs | 9 GB |
-| 50 million | 31 Mupd/s | 3⅓ GB | 120 Mupd/s | 6.9 secs | 28 GB |
-| 250 million | 30 Mupd/s | 14 GB | 114 Mupd/s | 17.3 secs | 76 GB |
-| 1000 million | 24 Mupd/s | 57 GB | OOME | 43 secs | NA |
-| 2500 million | 23 Mupd/s | 126 GB | Did not test | NA | NA |
-
-_*HashMap refers to ConcurrentHashMap, Chronicle refers to Chronicle Map_
-
-
-Key :
-RSS - Resident memory size. How much main memory was used.
-Mupd/s - Million write operations per second. i.e. put(key, value);
-
-
-Notes:
-* `ChronicleMap` was tested with a 32 MB heap, CHM was test with a 100 GB heap.
-* The `ChronicleMap` test had a small minor GC on startup of 0.5 ms, but not during the test.
- This is being investigated.
-* `ChronicleMap` was tested "writing" to a tmpfs file system.
-
-#### How does it perform when persisted?
-
-Chronicle Map also supports persistence. In this regard there is no similar class in the JDK.
-
-| Number of entries | Chronicle Throughput | Chronicle RSS |
-|------------------:|---------------:|---------:|
-| 10 million | 28 Mupd/s | ½ GB |
-| 50 million | 28 Mupd/s | 9 GB |
-| 250 million | 26 Mupd/s | 24 GB |
-| 1000 million | 1.3 Mupd/s | 85 GB |
-
-Notes:
-* Persistence was performed at a PCI-SSD which supports up to 230K IOPS and 900 MB/s write speed.
- This test didn't test the card to it's limit until the last test.
-* The kernel tuning parameters for write back are important here.
- This explains the suddern drop off and this is being investigated.
-
-The sysctl parameters used were approximately 10x the defaults to allow as many operations
-to be performed in memory as possible.
-
- vm.dirty_background_ratio = 50
- vm.dirty_expire_centisecs = 30000
- vm.dirty_ratio = 90
- vm.dirty_writeback_centisecs = 5000
-
-# Questions and Answers
-
-#### Question
-I'm searching for a Map implementation that is backed by either direct off-heap or a memory
-mapped file. I want to use it as write-once, read-n-times kind of cache.
-#### Answer
-The latest version of ChronicleMap has been optimised for this use case. Ie for heavy read to write ratios.
-Note: heavy writers will see about the same performance.
-
----
-
-#### Question
-The backing of to off-heap is only needed to prevent OOM-situations. Otherwise the process can use a big part of the available memory for the map.
-
-#### Answer
-Chronicle Map can be larger than main memory. In fact you might want to reduce the heap size to give chronicle map more memory in extreme cases. Ie because the map can't utilise on heap memory much.
-
----
-
-#### Question
-I stumbled over chronicle-map and it looks like it could do most of those things. Correct me if i'm wrong but
-``` java
-// would create the off-heap version
-ChronicleMapBuilder.of(A.class, B.class).create();
-```
-#### Answer
-Yes.
-
----
-
-#### Question
-``` java
-// the memory-mapped file version ?
- ChronicleMapBuilder.of(A.class, B.class).file(mapFile).create();
-```
-#### Answer
-Yes.
-
----
-
-#### Question
-But both versions write the complete map at every single moment to off-heap, right ?
-
-#### Answer
-Every key/value is off heap. There is no cache of objects on heap.
-
----
-
-#### Question
-Is there something in chronicle map which can have part of the map-content still in on-heap memory.
-
-#### Answer
-There are data structures which are on heap to manage the off heap data. This is typically less than 100 kB.
-
----
-
-#### Question
-Either serialized or deserialized. Performance wise do you think that would even matter ?
-#### Answer
-It matter a lot but you can get the performance close to on heap objects without the long GC pause times. Ie only the bit on the heap makes any difference.
-
----
-
-#### Question
-Last but not least... do you think chronicle is suitable for this simple purpose ?
-
-#### Answer
-Actually this is all it does really. We try to make it fast by keeping it simple.
-Eg it is not a cache and doesn't have expiries etc. There is no background thread managing it.
-In fact it is so simple that many methods are completely in lined.
-Ie if you look at a crash dump the chronicle methods have been completely in lined and don't appear in the stack trace any more. (The still appear in java stack traces)
-
----
-
-#### Question
-Or is there a lot of overhead for other functionality which i do not intend to use (yet) !?
-
-#### Answer
-You can share the data between processes on the same machine but this doesn't add overhead.
-You can add replication between machines. But this uses extra classes which are not used in the simple case ie it is as if they were not there, only an option.
diff --git a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java
index c46562886..a7081aec9 100755
--- a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java
+++ b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java
@@ -194,7 +194,7 @@ public interface ChronicleHashBuilder,
* efficient manner, for example as {@code long[]} arrays. Typical number of connections is
* 100-300, maximum is 3000. In this case chunk size of
* 30 * (8 bytes for each id) = 240 bytes would be a good choice: {@code
- * Map socialGraph = ChronicleMapOnHeapUpdatableBuilder
+ * Map socialGraph = ChronicleMapBuilder
* .of(Long.class, long[].class)
* .entries(1_000_000_000L)
* .averageValueSize(150 * 8) // 150 is average adjacency list size
@@ -232,7 +232,7 @@ public interface ChronicleHashBuilder,
/**
* Configures the target number of entries, that is going be inserted into the hash containers,
- * created by this builder. If {@link #maxBloatFactor(double)} is configured to {code 1.0}
+ * created by this builder. If {@link #maxBloatFactor(double)} is configured to {@code 1.0}
* (and this is by default), this number of entries is also the maximum. If you try to insert
* more entries, than the configured {@code maxBloatFactor}, multiplied by the given number of
* {@code entries}, {@link IllegalStateException} might be thrown.