From 29c8c9d385311a3283ef012305d15d2b27f6a48a Mon Sep 17 00:00:00 2001 From: teamcity Date: Fri, 22 Sep 2017 00:43:31 +0100 Subject: [PATCH 1/5] Updating to bom version 1.14.67 at 2017-09-21T23:43:31.482Z --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d1a65d85e..7b59cbef3 100755 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ net.openhft chronicle-bom - 1.14.51 + 1.14.67 pom import From 7cbc967284503648087715349ef3a4f668d1d4c4 Mon Sep 17 00:00:00 2001 From: teamcity Date: Fri, 22 Sep 2017 09:10:26 +0100 Subject: [PATCH 2/5] Updating to bom version 1.14.68 at 2017-09-22T08:10:26.183Z --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7b59cbef3..bc8f6b672 100755 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ net.openhft chronicle-bom - 1.14.67 + 1.14.68 pom import From 90e94e563d0a06f97797f1fe243734a262420bf7 Mon Sep 17 00:00:00 2001 From: epickrram Date: Fri, 6 Oct 2017 16:19:44 +0100 Subject: [PATCH 3/5] update java-parent-pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bc8f6b672..434d64e54 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ net.openhft java-parent-pom - 1.1.12 + 1.1.13 From 8f0dd3d875f62b5352671efbee1472584d3a0e57 Mon Sep 17 00:00:00 2001 From: epickrram Date: Wed, 11 Oct 2017 14:44:59 +0100 Subject: [PATCH 4/5] update bom version, add test for BytesMarshallable key/values --- pom.xml | 2 +- .../map/AbstractMarshallableKeyValueTest.java | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/test/java/net/openhft/chronicle/map/AbstractMarshallableKeyValueTest.java diff --git a/pom.xml b/pom.xml index 434d64e54..5527c745d 100755 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ net.openhft chronicle-bom - 1.14.68 + 1.14.79 pom import diff --git a/src/test/java/net/openhft/chronicle/map/AbstractMarshallableKeyValueTest.java b/src/test/java/net/openhft/chronicle/map/AbstractMarshallableKeyValueTest.java new file mode 100644 index 000000000..b4ee8f840 --- /dev/null +++ b/src/test/java/net/openhft/chronicle/map/AbstractMarshallableKeyValueTest.java @@ -0,0 +1,28 @@ +package net.openhft.chronicle.map; + +import net.openhft.chronicle.wire.AbstractMarshallable; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public final class AbstractMarshallableKeyValueTest { + + @Test + public void shouldAcceptAbstractMarshallableComponents() throws Exception { + final ChronicleMap map = ChronicleMapBuilder.of(Key.class, Value.class).entries(10). + averageKey(new Key()).averageValue(new Value()).create(); + + map.put(new Key(), new Value()); + + assertThat(map.get(new Key()).number, is(new Value().number)); + } + + private static final class Key extends AbstractMarshallable { + private String k = "key"; + } + + private static final class Value extends AbstractMarshallable { + private Integer number = Integer.valueOf(17); + } +} From 50912303cc8c57393b8c9df1c3c7b2ad1905a139 Mon Sep 17 00:00:00 2001 From: Neil Clifford Date: Thu, 28 Sep 2017 16:27:32 +0100 Subject: [PATCH 5/5] Neil, Jerry, Rob: re-work doco --- ReadMe.adoc | 83 +++ docs/CM_Compatibility_and_Versioning.adoc | 122 ++++ docs/CM_Download.adoc | 67 ++ docs/CM_FAQs.adoc | 27 + docs/CM_Features.adoc | 148 ++++ docs/CM_Replication.adoc | 244 +++++++ docs/CM_Replication_Stress_Test.adoc | 291 ++++++++ docs/CM_Tutorial.adoc | 671 ++++++++++++++++++ docs/CM_Tutorial_Behaviour.adoc | 342 +++++++++ docs/CM_Tutorial_Bytes.adoc | 404 +++++++++++ docs/CM_Tutorial_DataAccess.adoc | 164 +++++ docs/CM_Tutorial_Sized.adoc | 156 ++++ docs/CM_Tutorial_Understanding.adoc | 69 ++ docs/CM_Updates.adoc | 43 ++ README.md => docs/archived/README.md | 1 - docs/{ => archived}/compatibility.md | 0 docs/{ => archived}/versioning.md | 0 docs/images/CM_Overview.jpg | Bin 0 -> 87720 bytes .../Configure_Three_Way_Replication.png | Bin 0 -> 67305 bytes 19 files changed, 2831 insertions(+), 1 deletion(-) create mode 100755 ReadMe.adoc create mode 100755 docs/CM_Compatibility_and_Versioning.adoc create mode 100755 docs/CM_Download.adoc create mode 100755 docs/CM_FAQs.adoc create mode 100755 docs/CM_Features.adoc create mode 100755 docs/CM_Replication.adoc create mode 100755 docs/CM_Replication_Stress_Test.adoc create mode 100755 docs/CM_Tutorial.adoc create mode 100755 docs/CM_Tutorial_Behaviour.adoc create mode 100755 docs/CM_Tutorial_Bytes.adoc create mode 100755 docs/CM_Tutorial_DataAccess.adoc create mode 100755 docs/CM_Tutorial_Sized.adoc create mode 100755 docs/CM_Tutorial_Understanding.adoc create mode 100755 docs/CM_Updates.adoc rename README.md => docs/archived/README.md (99%) mode change 100644 => 100755 rename docs/{ => archived}/compatibility.md (100%) rename docs/{ => archived}/versioning.md (100%) create mode 100755 docs/images/CM_Overview.jpg create mode 100755 docs/images/Configure_Three_Way_Replication.png diff --git a/ReadMe.adoc b/ReadMe.adoc new file mode 100755 index 000000000..9be84048a --- /dev/null +++ b/ReadMe.adoc @@ -0,0 +1,83 @@ += Chronicle Map + +Chronicle Map is a super-fast, in-memory, non-blocking, key-value store, designed for low-latency, and/or multi-process +applications such as trading and financial market applications. +See <> for more information. + +The size of a Chronicle Map is not limited by memory (RAM), but rather by the available disk capacity. + +image::docs\images\CM_Overview.jpg[] + +=== Use cases +Chronicle Map is used in production around the +world for: + +• **real-time trading systems**. Chronicle Map +provides in-memory access speeds, and supports +ultra-low garbage collection. Chronicle Map can support the most demanding of applications. +• **highly concurrent systems**. Chronicle Map +supports multiple readers and writers, +distributed across multiple machines. + +=== Why use Chronicle Map? +Chronicle Map is: + +• **fast**. Millions of operations per second, with +low and stable microsecond latencies for reads and writes. Write queries scale well up +to the number of hardware execution threads in the server. Read queries never block each +other. +• **reliable**. Chronicle Software have a “chaos +monkey” test which verifies Chronicle Map +multi-master replication in the face of node +and network failures. The map can optionally be persisted to disk. +• **in production** at banks and hedge funds, +globally. +• **built using lessons learnt** from real-world +experience solving real-world problems. +• **open source** (standard version), and in use at +hundreds of sites around the world. + +=== Our offering +Chronicle Software provides full support for +Chronicle Map, consulting to help you make +best use of the product, and can also deliver +projects using a mix of our resources and +your own. + +=== Replication Environment Example +The following diagram shows an example of Chronicle Map replication over three servers (or sites). +Chronicle Map Replication is part of Chronicle Map (Enterprise Edition); a commercially supported +version of our successful open source Chronicle Map. + +image::docs\images\Configure_Three_Way_Replication.png[] + +Replication is multi-master, lock-free, redundant, deterministic, and eventually consistent. + +The writer can optionally wait for replication to occur across nodes or regions. + +NOTE: See <> for more information. + +''' +== Documentation +The Chronicle Map documentation comprises: + +.Documentation +|=== +|**Document**|**Purpose** +|<> +|Features description. +|<> +|Replication explanation. +|<> +|Tutorial. +|<> +|Frequently asked questions. +|<> +|Downloading the software. +|<> +|Updates from Chronicle Map version 2. +|<> +|Compatibility and Versioning description. +|=== + +Linked documentation is contained in the `docs` folder. diff --git a/docs/CM_Compatibility_and_Versioning.adoc b/docs/CM_Compatibility_and_Versioning.adoc new file mode 100755 index 000000000..a8c7a31a1 --- /dev/null +++ b/docs/CM_Compatibility_and_Versioning.adoc @@ -0,0 +1,122 @@ += Chronicle Map Compatibility and Versioning +Neil Clifford +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +== Java Implementation Compatibility + +=== Public API + +The *public API* comprises public classes that are included in +http://www.javadoc.io/doc/net.openhft/chronicle-map/[Javadoc]. + +NOTE: Other public classes are not subject to +the following rules; they may be changed in any way, or even removed. + + - Class and method names, types, and number of parameters in already existing methods, should not be changed. + + - Any methods can be added to the classes and interfaces that are not subject for + subclassing or implementing by the library users; such as `ChronicleMap`, `ChronicleMapBuilder`, + `MapEntry`. They must be added in such a way that any existing code, calling methods on those classes still compiles; and does not + break due to ambiguity in method resolution. + + - Only non-abstract (concrete or default) methods can be added to the classes and interfaces that are subject for subclassing by the library users; such as `MapMethods`, `MapEntryOperations`, `SizedReader`. + +=== Behaviour + +Semantics and actual effects of API calls may change between Chronicle Map versions with +different `` number versioning. + +For example, default serialization forms of key or value objects of some types could change, "high level" configurations in `ChronicleMapBuilder` could produce +Chronicle Maps with different "low level" configurations, etc. + +Such changes should be documented in the release notes. + +=== Persistence File Binary Form + +A persisted Chronicle Map store (a binary file), produced by a production release of the library, should be accessible and updatable by any subsequent production release of the library in the `3.x` branch; unless the older version has bugs that lead to data corruption. + +To support this: + + - Serialization forms of any classes should not be changed. For example, when a marshaller class (for example, `StringBytesReader`) of objects of some type is changed, and its serialization form should be changed, a new class called `StringBytesReader_3_5` (`5` is the `` number of the version of the library, in which this change is made) should be created, and put along with older class. + + - When the specification revision is updated (where the binary form could be completely changed), a new version of the root implementation class (say, `VanillaChronicleMap_3_5`) should be created and put along with older version, which should behave the same as before. + +=== Replication Protocol + +The replication protocol may change between Chronicle Map versions with different `` numbers. + +Chronicle Map nodes should only communicate with other nodes of the same version. + +Chronicle Map nodes running different versions of the library should not communicate with each other. This may lead to failures, but not to data corruption on either of the nodes. + +== Specification Compatibility + +The Chronicle Map Data Store specification can be updated without changing the revision while it remains backward compatible. Backward compatibility means that the implementation of the newer version of the specification can access and update any persisted Chronicle Map store that was produced by an older version. + +In addition, potential *concurrent* accessing implementations of older versions ("within" the same revision) should not lose correctness or corrupt the data. + +If more substantial changes to the specification are required, then the revision number should be updated. + +The binary form of Chronicle Map and its algorithms could change between revisions to any degree, but link:../spec/1-design-goals.md#guarantees[guarantees] provided by the specification are not weakened. + +== Java implementation Versioning + +The versioning format is as follows: + +`..` + +For example: +`3.14.1` + + +=== The number should be updated, when: + + - **a feature, or public API** method is added. + + - **the behaviour** of `ChronicleMap`, `ChronicleMapBuilder`, or any other class or method or subsystem, is semantically changed. + + For example, when configuration heuristics in `ChronicleMapBuilder` are improved, and + `ChronicleMapBuilder` configured the same way constructs a `ChronicleMap` with a different number of segments, or segment's capacity. + + - **any dependency is updated**; even if only the `` number is updated. For example, `chronicle-bytes` dependency is updated from `1.1.1` to `1.1.2`. + + This rule does not apply to dependencies to the umbrella POMs which merely determine versions of other dependencies (namely, `chronicle-bom` and `third-party-bom`), if the versions of actual dependencies do not change between those versions of umbrella POMs. + + Also, this rule does not apply to dependencies with Maven scopes of `test` or `provided`. + + - **bug fixes, performance optimization, or feature additions** that requires changes to: + ** the binary, or serialized, form of Chronicle Map data store; therefore revising the Chronicle Map data store specification. + ** the binary replication protocol, or the serialized form of some marshaller classes, that require creation of new versions of classes. + +=== The number should not be updated when: + - **a bug is fixed**, in a way that does not make any working code to change its behaviour semantically. + - **performance is optimized**, changing its performance characteristics, + but not semantics. + +During development, and after the release of the version `3.y.z`, the library should be versioned `3.y.-SNAPSHOT`. + + + +As soon as changes are made that are substantial enough for the next released version +to update the `` number, then the version should be changed to `3..0-SNAPSHOT`, *in the same +commit operation* that these changes are made. + +Suffixes `-alpha`, `-beta`, `-rc`, mean it is a pre-production release. + +NOTE: If the number is not to be updated, the next release updates the `` number. + +== Specification Versioning + +The first Chronicle Map 3 production release designates the Chronicle Map data store specification; revision 1. + +When the specification needs to be updated in a way that is not backward compatible, the revision should be updated (revision `2`, `3`, ...) and the Java implementation of this revision should have its '' version number updated. + +During development, and after the release of the revision `x`, while the specification is backward compatible, it should remain versioned `x`. In the same commit operation where the specification becomes +incompatible, its revision should be set to `-pre` until the next production release. + +Releases and updates to the `` number in the library version should be synchronized with updates to the specification revision number. + + +''' +<<../ReadMe.adoc#,Back to ReadMe>> diff --git a/docs/CM_Download.adoc b/docs/CM_Download.adoc new file mode 100755 index 000000000..9da022f8e --- /dev/null +++ b/docs/CM_Download.adoc @@ -0,0 +1,67 @@ += Download the library +Neil Clifford +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +== Prerequisites + +To run Chronicle Map, please ensure you have first installed: + +* Java (1.8.0_91 or later) +* Apache Maven (3.2.3 or later) + +== License + +Chronicle Map is distributed under `LGPLv3`. If you want to obtain this software under a more permissive +license, please contact mailto:sales@chronicle.software. + +== Maven Artifact Download + +[#image-maven] +[caption="", link=https://maven-badges.herokuapp.com/maven-central/net.openhft/chronicle-map] +image::https://maven-badges.herokuapp.com/maven-central/net.openhft/chronicle-map/badge.svg[] + + +``` xml + + net.openhft + chronicle-map + + +``` + +Click here to get the http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.openhft%22%20AND%20a%3A%22chronicle-map%22[Latest Version Number]. + +== Maven Snapshot Download +If you want to try out the latest pre-release code, you can download the snapshot artifact manually +from https://oss.sonatype.org/content/repositories/snapshots/net/openhft/chronicle-map/. + +A better way, is to add the following to your `setting.xml` file to allow Maven to download snapshots: + +``` xml + + Snapshot Repository + Snapshot Repository + https://oss.sonatype.org/content/repositories/snapshots + + true + + +``` +You can define the snapshot version in your `pom.xml`; for example: + +```xml + + net.openhft + chronicle-map + + +``` + +''' +<<../ReadMe.adoc#,Back to ReadMe>> diff --git a/docs/CM_FAQs.adoc b/docs/CM_FAQs.adoc new file mode 100755 index 000000000..19810ede6 --- /dev/null +++ b/docs/CM_FAQs.adoc @@ -0,0 +1,27 @@ += Chronicle Map Frequently Asked Questions + +== Question + +I am investigating Chronicle Map as a potential replacement of `Redis`; with concurrency in mind. + +In our architecture, we would be looking to replace a "large" `Redis` instance that currently has multiple clients connecting to it, causing latency pileups due to ``Redis``'s blocking nature. + +The issue is that we need to make requests in random batches of ~1000. With `Redis` we are able to make a single request via a `Lua` script (or multi-get / multi-set commands) and receive a single response. In the documentation on Chronicle Map`s stateless client, I see that the remote calls are blocking, and can be made only one key at a time; so for us the solution is not obvious. + +While I am considering passing off each individual key task to a threadpool running `x` blocking threads at a time, I wonder if there might be a better solution, that could take advantage of doing RPC in batches, and perhaps work asynchronously. As I do not see this available currently, my question is whether this is an enhancement that you might consider, or if you could perhaps point me to if/how we could write our own solution for doing this? + +Also, is there a reason these `1000` `gets` have be done serially in one thread? Why not submit `1000 get()` tasks to a pool of say `20` threads. Wouldn't this improve throughput and reduce latency? + +== Answer + +The stateless client has been replaced with Chronicle Engine. The stateless client is not supported in ChronicleMap 3.x. For more information, please refer to Chronicle Engine FAQ's. + +For `get()s`, parallelizing will reduce costs. For `put()s`, if you have concurrency requirements, that is, multi-key lock before updating all of them, it should be in one thread. + +You would not gain a performance benefit by using batches, unless you are compressing the batch of data. All the data will have to be sent via TCP, even if it is in a batch. + +NOTE: Under high load, the Chronicle Map stateless client consolidates many small TCP requests into a single request when run with a number of threads. + + +''' +<<../ReadMe.adoc#,Back to ReadMe>> diff --git a/docs/CM_Features.adoc b/docs/CM_Features.adoc new file mode 100755 index 000000000..fe2b87358 --- /dev/null +++ b/docs/CM_Features.adoc @@ -0,0 +1,148 @@ += Chronicle Map - Features +Neil Clifford +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +Chronicle Map is an in-memory, key-value store, designed for low-latency, and/or multi-process +applications, such as trading and financial market applications. + +== Features + - **Ultra low latency**: Chronicle Map targets median latency of both read and write queries of less + than 1 microsecond in https://github.com/OpenHFT/Chronicle-Map/search?l=java&q=perf&type=Code[certain tests]. + + - **High concurrency**: Write queries scale well up to the number of hardware execution threads in + the server. Read queries never block each other. + - **Persistence to disk** - (Optional) + - **Multi-master replication** - (Optional, commercial functionality) - Eventually-consistent, fully-redundant, asynchronous replication across + servers, "last write wins" strategy by default, allows to implement custom https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type[state-based CRDT] strategy. + +== Unique features + - Multiple processes can access a Chronicle Map concurrently. At the same time, + the data store is *in-process* for each of the accessing processes. Out-of-process approach to IPC + is simply incompatible with Chronicle Map's median latency target of < 1 μs. + + - Replication *without logs*, with constant footprint cost, guarantees progress even if the network + doesn't sustain write rates. + + +NOTE: See <> for more information. + +**Chronicle Map** has two meanings: + +- https://github.com/OpenHFT/Chronicle-Map/blob/master/spec[language-agnostic data store (specification)]. + +- https://github.com/OpenHFT/Chronicle-Map/blob/master/src[implementation of this data store for the JVM (source)]. Currently, this is the only implementation. + +=== From the Java perspective +`ChronicleMap` 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 which those solutions lack. + + - Moving parts of the application-state out of the Java heap for either: + * Reducing the heap size, for reducing garbage collection pressure, or fitting 32 GB, for using `CompressedOops`. + * Inter-process communication. + * Persistence. + * 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; Chronicle Map can be persisted to disk, but with no guarantee as to how frequently this + happens. This is under the control of the OS. All data is guaranteed to be written to disk when the Map is closed. + - **Clustering** and **replication** for Chronicle + Map is provided by https://github.com/ChronicleEnterprise/Chronicle-Map-Enterprise[Chronicle Map Enterprise]. + +== What is the data structure of Chronicle Map? +Simply put, a Chronicle Map data store is a big chunk of shared memory (optionally mapped to disk). + +It is split into independent segments; each segment has: + + - an independent memory allocation for storing the entries + + - a hash table for search + + - a lock in shared memory (implemented via CAS loops) for managing concurrent access. + +See https://github.com/OpenHFT/Chronicle-Map/blob/master/spec[ Chronicle Map data store design overview] for more information. + +== Chronicle Map is not + + - A document store. There are no secondary indexes. + - A https://en.wikipedia.org/wiki/Multimap[multimap]. + Using a Chronicle Map collection as a multimap is technically possible, but often leads to problems. See http://stackoverflow.com/a/36486525/648955[StackOverflow answer] for details. + Developing a proper multimap, using + Chronicle Map's design principles is possible. + +Please contact us at mailto:sales@chronicle.software[sales@chronicle.software] if + you would consider sponsoring such development. + +== Chronicle Map does not support: + + - range queries. + - iteration over the entries in alphabetical order. + - sorting of keys. + - LRU entry eviction. + +== Features + +.Features +|=== +|Feature |Availability + +|In-memory off-heap Map +|Open source +|Persistence to disk +|Open source +|Remote calls +|Commercially available +|Eventually-consistent replication (100% redundancy +|Commercially available +|Synchronous replication +|Commercially available +|Partially-redundant replication +|On demand +|Entry expiration timeouts +|On demand + +|=== + +NOTE: For access to commercially available features please contact mailto:sales@chronicle.software[sales@chronicle.software]. + +== Peer projects + - https://github.com/OpenHFT/Chronicle-Engine[Chronicle Engine]. Reactive processing framework + supporting Chronicle Map as a backend. + + - http://vanillajava.blogspot.com/2015/09/chronicle-journal-customizable-data.html[Chronicle Journal]. Another + key-value built by Chronicle Software, with different properties. + +== Community support: + - https://github.com/OpenHFT/Chronicle-Map/issues[Issues] + - https://groups.google.com/forum/#!forum/chronicle-map[Chronicle Map mailing list] + - http://stackoverflow.com/tags/chronicle-map[Stackoverflow] + - https://plus.google.com/communities/111431452027706917722[Chronicle User's group] + - http://jrvis.com/red-dwarf/?user=openhft&repo=Chronicle-Map[Chronicle Map usage heatmap] + + +''' +<<../ReadMe.adoc#,Back to ReadMe>> diff --git a/docs/CM_Replication.adoc b/docs/CM_Replication.adoc new file mode 100755 index 000000000..bfaa590cc --- /dev/null +++ b/docs/CM_Replication.adoc @@ -0,0 +1,244 @@ += Chronicle Map Replication (Enterprise Edition feature) +Neil Clifford +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +== Enterprise Edition +Chronicle Map Replication is part of Chronicle Map (Enterprise Edition); a commercially supported version of our successful open source Chronicle Map. Extended features include: + +- *Replication* to ensure real-time backup of all your map data. +- *Resilience* support for robust fail-over and disaster recovery environments. + +In addition, you will be fully supported by our technical experts. + +For more information on Chronicle Map (Enterprise Edition), please contact mailto:sales@chronicle.software[sales@chronicle.software]. + +To ensure that Chronicle Map's multi-master replication works under extreme circumstances, we have +built a stress test to ensure this using the principles introduced by Netflix with their "chaos monkey". +For more information on the stress test see <>. + += Replication + +Chronicle Map Replication is a multi-master replicated data store. Chronicle Map supports both TCP and UDP replication + +image::http://openhft.net/wp-content/uploads/2014/07/Chronicle-Map-TCP-Replication_simple_02.jpg[TCP/IP Replication] + +== TCP / UDP Background +TCP/IP is a reliable protocol. This means that, unless you have a network failure or hardware outage, the data is guaranteed to arrive. TCP/IP provides point-to-point connectivity. For example, if the message was sent to 100 hosts, the message would have to be sent 100 times. + +With UDP, the message is only sent once. This is ideal if you have a large number of hosts, and you wish to broadcast the same data to each of them. However, one of the big drawbacks with UDP is that it is not a reliable protocol. This means, if the UDP message is broadcast onto the network, the hosts are not guaranteed to receive it; so they can miss data. + +Some solutions attempt to build resilience into UDP, but arguably, this is in effect reinventing TCP/IP. + +== How to setup UDP Replication +On a good quality wired LAN, when using UDP, you will rarely miss messages. Nevertheless, this is a risk that we suggest you do not take. We suggest that whenever you use UDP replication, you use it in conjunction with throttled TCP replication. Therefore, if a host misses a message over UDP, it will pick it up later via TCP/IP. + +== TCP/IP Throttling +We are careful not to swamp your network with too much TCP/IP traffic. We do this by providing a throttled version of TCP replication. This works because Chronicle Map only broadcasts the latest update of each entry. + +== How Chronicle Map Replication works +Chronicle Map provides multi-master hash-map replication. This means that each remote map mirrors its changes over to another remote map. Neither map is considered to be the master store of data. Each map uses timestamps to reconcile changes. + +We refer to an instance of a remote map as a **node**. Each node can be connected to up to 128 other nodes. + +The data that is stored locally in each node becomes eventually consistent. So changes made to one node, for example by calling `put()`, will be replicated over to the other node. + +To achieve a high level of performance and throughput, the call to `put()` will not block. + +With `ConcurrentHashMap`, it is typical to check the return code of some methods to obtain the old value; for example, `remove()`. + +Due to the loose coupling, and lock-free nature of this multi-master implementation, the return value is only the old value on the node's local data store. In other +words, the nodes are only concurrent locally. Another node, performing exactly the same operation, may return a different value. However, reconciliation will ensure that all the maps +will become eventually consistent. + +== Reconciliation +If two or more nodes receive a change to their maps for the same key, but with different values, say by a user of the maps, calling the `put(key,value)`, then, initially each node will update its local store, and each local store will hold a different value. + +The aim of multi-master replication is +to provide eventual consistency across the nodes. Using multi-master replication, whenever a node is changed it will notify the other nodes of its change; we refer to this notification as an event. + +The event will hold a timestamp indicating the time that the change occurred. It will also hold the state transition; in this case it was a `put` with a key and value. + +Eventual consistency is achieved by looking at the timestamp from the remote node. If, for a given key, the remote node's timestamp is newer than the local node's timestamp, then the event from the remote node will be applied to the local node; otherwise, the event will be ignored. + +Since none of the nodes is a primary, each node holds information about the other nodes. For a specific node, its own identifier is referred to as the 'localIdentifier'. The identifiers of other nodes are the 'remoteIdentifiers'. + +On an update, or insert, of a key/value, the node pushes the information about the change to the remote nodes. The nodes use non-blocking Java NIO I/O, and all replication is done on a single thread. + +However, there is a specific edge case. If two nodes update their map at precisely the same time with different values, we have to deterministically resolve which update wins. This is because eventual +consistency mandates that both nodes should end up holding the same data locally. + +Although it is rare that two remote +nodes receive an update to their maps at exactly the same time, for the same key, we have to handle this edge case. We cannot therefore rely on timestamps alone to reconcile +the updates. + +Typically, the update with the newest timestamp should win, but in this example both timestamps are the same, and the decision made to one node should be identical to the decision made to the other. This dilemma is resolved by using a node identifier. The node identifier is a unique +'byte' value that is assigned to each node. When the timestamps are the same, the remote node with the smaller identifier will be preferred. + +== Multiple Processes on the same server with Replication + +On a single server, if you have a number of Java processes, and then within each Java process you create an instance of a Chronicle Map which binds to the same underline 'file', they exchange data via shared memory, rather than by TCP or UDP replication. + +If an instance of Chronicle Map, which is not performing TCP Replication, is updated, then this update can be picked up by another instance of Chronicle Map. This other Chronicle Map instance could be TCP replicated. In such an example, the TCP replicated Chronicle Map instance would then push the update to the remote nodes. + +Likewise, if the TCP replicated Chronicle Map instance received an update from a remote node, then this update would be immediately available to all the instances of Chronicle Map on the server. + +== Identifier for Replication +If you are only replicating your Chronicle Map instances on the same server, then you do not have to set up TCP and UDP replication. You also do not have to set the identifiers; as the identifiers are only used for the resolution of conflicts amongst remote servers. + +If however, you wish to replicate data between two or more servers, then all of the Chronicle Map instances, including those not actively participating in TCP or UDP replication, must have their identifiers set. + +The identifier must be unique to each server. Each ChronicleMap on the same server must have +the same identifier. The reason that all Chronicle Map instances must have the identifier set, is because +the memory is laid out slightly differently when using replication, so even if a map is not actively performing TCP or UDP replication itself, if it wishes to replicate with one that is, it must have its memory laid out in the same way to be compatible. + +If the identifiers are not set up uniquely, then the updates will be ignored. For example, +a Chronicle Map instance that is set up with the identifier equal to '1', will ignore all events which contain the remote identifier of '1'. In other words, Chronicle Map replication ignores updates which have originated from itself. This is to avoid the circularity of events. + +When setting up the identifier you can use values from `1` to `127`. + +The identifier is setup on the builder as follows: + +```java +TcpTransportAndNetworkConfig tcpConfig = ... +map = ChronicleMapBuilder + .of(Integer.class, CharSequence.class) + .replication(identifier, tcpConfig) + .create(); +``` + +== Configuration + +Configuration of map nodes is done either, programmatically, through configuration files, or by a combination of both. + +=== Programmatically +The example below shows how to set up two map instances that use TCP to replicate entries between them: + +[source, java] +.... + +// set up some socket aliases +TCPRegistry.createServerSocketChannelFor("host.port1", "host.port2"); + +// create replicated maps +final ReplicatedMap replicaOne = createMap(1, "host.port1"); +final ReplicatedMap replicaTwo = createMap(2, "host.port2"); + +// start replication event loops +replicaOne.start(); +replicaTwo.start(); + +// register maps for replication events +replicaOne.beginReplicationFromPeer("host.port2", (byte) 2); +replicaTwo.beginReplicationFromPeer("host.port1", (byte) 1); + +// allow replication to establish +Jvm.pause(250L); + +// access the underlying maps +final Map mapOne = replicaOne.getMap(); +final Map mapTwo = replicaTwo.getMap(); + +// enter some data into mapOne +for (long i = 0; i < 100; i++) { + mapOne.put(i, i * 2); +} + +// enter some different data into mapTwo +for (long i = 100; i < 200; i++) { + mapTwo.put(i, i * 7); +} + +// wait for replication between nodes (conservative) +Jvm.pause(1000L); + +System.out.printf("mapOne has %d entries, expected 200%n", mapOne.size()); +System.out.printf("mapTwo has %d entries, expected 200%n", mapTwo.size()); +.... + +=== Configuration file +The following example uses a basic `yaml` configuration file to define defaults for the map: + +[source, yaml] +.... +!ReplicatedMapCfg { + hostId: 1, # host id must be unique within the cluster + listenAddressHostPort: "localhost:9301", # address on which to listen for inbound replication requests + entries: 10000, # entries hint suggesting the initial size of the map + keyClass: !type long, # class of the key type + valueClass: !type long, # class of the value type + exampleKey: 17, # an example key (used to size the map) + exampleValue: 37, # an example value (used to size the map) + name: testLongToLongMap, # a name for the map (used to determine filename) + locator: "csp://localhost:9301/map/test", # a locator, used for discovery + mapFileDataDirectory: target/mapData, # directory where map file should be stored + mapLogDirectory: target/mapReplicationLogs, # directory where replication events should be recorded + keysAreConstantSize: true, # determines how keys are serialised + valuesAreConstantSize: true # determines how values are serialised +} +.... + +After putting 100 entries into each distinct map, the example demonstrates that the entries have been replicated so that each map +now contains 200 entries. + +This example is available in the repository, https://github.com/ChronicleEnterprise/Chronicle-Map-Enterprise/blob/master/src/test/java/software/chronicle/enterprise/map/example/MultiMasterMapReplicationExampleMain.java[MultiMasterMapReplicationExampleMain.java] + + +== Replication event logging + +Chronicle Map Enterprise can be configured to log all replication events to a Chronicle Queue for auditing purposes. + +Currently, a map can be configured to log all *outgoing* events that it sends to remote peers. + +The example below shows the message flow for a map with a single remote peer receiving replication events: + +[source, java] +.... +.. header omitted + +targetHostId: 2 # message destination +registerForReplication: { + receiverHostId: 1, # local peer id + keyClassName: java.lang.Long, # key type + valueClassName: java.lang.Long, # value type + sendAllEventsAfter: 0 # bootstrap timestamp +} + +.. + +# corresponding registration message from remote peer +targetHostId: 1 +registerForReplication: { + receiverHostId: 2, + keyClassName: java.lang.Long, + valueClassName: java.lang.Long, + sendAllEventsAfter: 0 +} + +.. + +# accept replication connection +targetHostId: 2 +acceptingReplicationRequest: { + acceptingHostId: 1 +} + +.. + +# encoded replication update sent to remote peer +targetHostId: 2 +replicatedEntry: !!binary AYChq5LqwKXqFAFOEwAAAAAAAAD/////////fw== +.... + +This output can be generated by running the example program, https://github.com/ChronicleEnterprise/Chronicle-Map-Enterprise/blob/master/src/test/java/software/chronicle/enterprise/map/example/ReplicationEventLoggingExampleMain.java[ReplicationEventLoggingExampleMain.java] + +''' +For the replication stress test, see <>. + +''' +<> diff --git a/docs/CM_Replication_Stress_Test.adoc b/docs/CM_Replication_Stress_Test.adoc new file mode 100755 index 000000000..dbc7cfb78 --- /dev/null +++ b/docs/CM_Replication_Stress_Test.adoc @@ -0,0 +1,291 @@ += Replication Stress test +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +The following stress test is provided for Chronicle Map Enterprise Edition. The test can be configured to run across multiple machines, with varying read/write workloads. + +NOTE: This document assumes that you have access to the restricted https://github.com/ChronicleEnterprise/Chronicle-Map-Enterprise-Demo[Chronicle-Map-Enterprise-Demo] repository. + +== Single master, multiple slaves + +To execute a test, open the the `Example4` folder, and then execute the following command from a `bash` shell: + +[source,bash] +.... +$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml +.... + +This command runs a test, using the specified configuration, which models a single master +node working with three slave nodes. All nodes run within the same JVM for simplicity. + +The test reports throughput metrics in terms of reads, writes, and network traffic. For example: + +[source,bash] +.... +Write stress-test-1: 237 inserts/sec, 702 updates/sec (total 940/sec) +Read stress-test-2: 2175 reads/sec, 0.00% were null +Read stress-test-3: 2177 reads/sec, 0.00% were null +Read stress-test-4: 2176 reads/sec, 0.00% were null +Node 3 received 1061 events/sec, 151KB/sec from remote 1 +Node 2 received 1062 events/sec, 151KB/sec from remote 1 +.... + +Every minute, the test pauses the writer, and checks that all maps within the cluster +contain the same data; that is, they all have the same `keySet`, and the value for each key is +identical throughout the cluster. For example: + +[source,bash] +.... +Starting map checker due to time of 11:37:30.035 +Checking maps for equality +All maps contained the same data +.... + + +== Running the test in multiple JVMs + +To spread the workload over multiple JVMs, use the `-h` parameter, which specifies the nodes that should be started in a particular JVM. The `singleMasterStressTest.yaml` configuration file contains 4 nodes (1 master, 3 slaves). The example below spreads these nodes across two processes. + +. On one terminal, run the following command: + + [source,bash] + .... + $ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 1 -h 2 + .... + ++ +then wait for the message notifying that the test is starting: + + [source,bash] + .... + Built stress test artifacts, running test.. + .... + +. On another terminal, run the following command: + + [source,bash] + .... + $ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3 -h 4 + .... + ++ + +In both terminals, throughput and map-equality checking will be reported. + +== Multiple masters, multiple slaves + +To test a multi-master scenario, where several different nodes are written to, execute the following command: + +[source,bash] +.... +./run_stress_test.sh -c src/main/resources/multipleMasterStressTest.yaml +.... + +== Test configuration + +Stress test scenarios are configured using the supplied `yaml` file. Using the examples +included in this repository as a template, it is possible to configure custom scenarios +to model real-world workloads. + +The configuration is comprised of two components: + +1. Test parameters +2. Cluster configuration + +Test parameters are described below: + +[source,yaml] +.... +!TestAndClusterConfig { + testConfig: !StressTestConfig { + numberOfSlaveNodes: 3, # map instances that are read from + numberOfMasterNodes: 1, # map instances that are written to + updatesPerSecond: 1000, # writes per instance per second + queriesPerSecond: 2500, # reads per instance per second + insertToUpdateRatio: 0.25, # ratio of remove/insert vs update operations + keySpaceSize: 5000, # size of the key space (uniform random integers) + valueSizeInBytes: 128, # payload size of value object + testLengthMinutes: 2 # duration of a test + }, +.... + +== Running tests across multiple physical machines + +To demonstrate how to run tests across different machines, the following details are defined: + +Hosts within the cluster: + +[frame="topbot",options="header,footer"] +|======================================= +| host | host_id | IP | role +| A | 1 |10.0.2.1 | master +| B | 2 |10.0.2.14 | master +| C | 3 |10.0.2.30 | slave +| D | 4 |10.0.2.22 | slave +|======================================= + +NOTE: Substitute your own settings as appropriate. + + +**TCP port used for replication**; this port will need to be open on all hosts: + +`13000` + +**Configuration file name**: + +`multiHost.yaml` + + +The cluster configuration in `multiHost.yaml` would be: + + +[source,yaml] +.... + clusterConfig: !Clusters { + myCluster: { + config: !ReplicatedMapCfg { + entries: 10000, + keyClass: !type long, + exampleKey: 17, + name: stress-test-map, + keysAreConstantSize: true, + valuesAreConstantSize: false, + keyDataAccess: !LongDataAccess { + }, + keyReader: !LongMarshaller { + }, + }, + topology: { + context: !MapClusterContext { + wireType: TEXT, + }, + hostA: { + hostId: 1, + connectUri: "10.0.2.1:13000" + }, + hostB: { + hostId: 2, + connectUri: "10.0.2.14:13000" + }, + hostC: { + hostId: 3, + connectUri: "10.0.2.30:13000" + }, + hostD: { + hostId: 4, + connectUri: "10.0.2.22:13000" + } + }, + monitoring: { + replicationEventListener: !LoggingReplicationEventListener + } + } +.... + + +=== Packaging the test + +To run the test on remote machines, the stress test can be packaged up using the following command: + +[source,bash] +.... +$ ./package_stress_test.sh +.... + +This command generates a `tar` archive in the current directory, containing everything required to run a stress test. + +Copy the resulting `tar` file to the remote server, unpack using `tar xf`, and then run the following command: + +[source,bash] +.... +./run_stress_test.sh -c [config-file] +.... + +To run the example multi-machine configuration described above, the following commands would be run on each host: + +[frame="topbot",options="header,footer"] +|======================================= +| host | host_id | IP | command +| **A** | `1` |`10.0.2.1` | `./run_stress_test.sh -c multiHost.yaml -h 1` +| **B** | `2` |`10.0.2.14` | `./run_stress_test.sh -c multiHost.yaml -h 2` +| **C** | `3` |`10.0.2.30` | `./run_stress_test.sh -c multiHost.yaml -h 3` +| **D** | `4` |`10.0.2.22` | `./run_stress_test.sh -c multiHost.yaml -h 4` +|======================================= + + +== Destructive testing + +There are two ways of recovering from node failure: + +1. Delete previous map data, and rejoin the cluster. + +2. Recover previous map data, and rejoin the cluster. + +If the total dataset contains a large proportion of infrequently updated records, then it is preferable to use option 2. + +If the entire key-set is updated frequently, then there is little difference between the two methods. + +Both scenarios can be simulated using the stress test. + +=== Delete existing data and rejoin (Option 1) + +. Start one or more slave processes: + + [source,bash] + .... + $ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3 + .... + +. Start one or more master nodes: + + [source,bash] + .... + $ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 1 + .... + +. When replication has been established, kill the slave process; for example, by using `Ctrl+c`. + +. Restart the slave process (see step 1). +By default, the stress test code deletes existing map data files for the specified hosts +on startup. + + [source,bash] + .... + $ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3 + .... + ++ +There should be a noticable difference in throughput after the restart, as +the slave map receives the entire data-set from the master: + + [source,bash] + .... + # usual operation + Node 3 received 1153 events/sec, 153KB/sec from remote 1 + + # slave process killed and restarted + + # higher throughput as slave catches up + Node 3 received 1646 events/sec, 234KB/sec from remote 1 + .... + + +=== Recover existing data and rejoin (Option 2) + +The stress test can be configured, using the `-r` parameter, to recover from existing data on startup. + +Perform the same procedure as in Option 1, step 4, but this time when restarting the slave client use `-r`: + +[source,bash] +.... +$ ./run_stress_test.sh -c src/main/resources/singleMasterStressTest.yaml -h 3 -r +.... + +This ensures that the existing map is recovered from disk. This may save network traffic, depending on how much data has been updated in the master map during downtime. + +<> \ No newline at end of file diff --git a/docs/CM_Tutorial.adoc b/docs/CM_Tutorial.adoc new file mode 100755 index 000000000..2a035f1ce --- /dev/null +++ b/docs/CM_Tutorial.adoc @@ -0,0 +1,671 @@ += Chronicle Map Tutorial +Neil Clifford +:toc: macro +:toclevels: 2 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +This document describes the Chronicle Map tutorial supplied with the project. + + +== 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`. + +== In-memory Chronicle Map +The following code snippet creates an in-memory Chronicle Map store, to store about 50,000 _city name -> postal code_ mappings. + +It is accessible within a single JVM process; the process in which it was created. The data is accessible while the process is alive. When the process is terminated, the data is cleared. + + +``` java +import net.openhft.chronicle.map.* +..... + +interface PostalCodeRange { + int minCode(); + void minCode(int minCode); + + int maxCode(); + void maxCode(int maxCode); +} + +ChronicleMapBuilder cityPostalCodesMapBuilder = + ChronicleMapBuilder.of(CharSequence.class, PostalCodeRange.class) + .name("city-postal-codes-map") + .averageKey("Amsterdam") + .entries(50_000); +ChronicleMap cityPostalCodes = + cityPostalCodesMapBuilder.create(); + +// Or shorter form, without builder variable extraction: + +ChronicleMap cityPostalCodes = ChronicleMap + .of(CharSequence.class, PostalCodeRange.class) + .name("city-postal-codes-map") + .averageKey("Amsterdam") + .entries(50_000) + .create(); +``` + +== Persisted Chronicle Map + +You can amend this code to create a persisted Chronicle Map by replacing `.create()` calls with `.createPersistedTo(cityPostalCodesFile)`. Use a persisted Chronicle Map if you want it to, either: + + - outlive the process it was created within; for example, to support hot application redeployment. + - be accessible from multiple concurrent processes on the same server. + - persist the data to disk. + +The `cityPostalCodesFile` has to represent the same location on your server for all the Java +processes that wish to access this Chronicle Map instance. For example, `System.getProperty("java.io.tmpdir") + "/cityPostalCodes.dat"`. + +The name and location of the file is entirely up to you. + +NOTE: When you create a `ChronicleMap` instance with `.createPersistedTo(file)`, and the specified +file already exists in the system, you open a view to the existing Chronicle Map data store from +this JVM process, rather than creating a new Chronicle Map data store. That means that the data store may already contain some entries. No special action with the data is performed during such an operation. If you want to clean up corrupted entries, and ensure that the data store is in correct state, see the <> section. + + +== ChronicleMap instance vs Chronicle Map data store + +In this tutorial, the term `ChronicleMap` instance (or simply `ChronicleMap`) is used to refer to an on-heap object providing access to a Chronicle Map data store (or Chronicle Map key-value store, or Chronicle Map store, or simply Chronicle Map, with space between two words in contrast to `ChronicleMap`), which could be purely in-memory, or persisted to disk. + +Currently the Java implementation doesn't allow creation of multiple accessor ChronicleMap` objects for a single *in-memory Chronicle Map store*; there is always a one-to-one relationship. + +A *persisted Chronicle Map store*, however, does allow creation of multiple accessor `ChronicleMap` instances; either within a single JVM process (although it is not recommended), or from concurrent JVM processes. + +When no processes access the file, it could be moved to another location in the system, or +even to another server. It could even run on different operating systems. When opened from another location you will observe the same data. + +If you don't need the Chronicle Map instance to survive the server restart (that is, you don't need +persistence to disk; only multi-process access), mount the file on http://en.wikipedia.org/wiki/Tmpfs[tmpfs]. For example, on `Linux` it is as easy as placing you file in `/dev/shm` directory. + + +== Configure entries + +You must configure .entries(entries) -- to support the maximum number of entries in the Chronicle Map. Try to configure the entries so that the created Chronicle Map is going to serve about 99% of requests. + +You should not 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` do not have a notion of load factor. + +See `ChronicleMapBuilder#entries()` in http://www.javadoc.io/doc/net.openhft/chronicle-map/[Javadocs] for more information. + +Once a `ChronicleMap` instance is created, its configurations are sealed and cannot be changed +using the `ChronicleMapBuilder` instance. + + +== Single `ChronicleMap` instance per JVM +If you want to access a Chronicle Map data store +concurrently within a Java process, you should not create a separate `ChronicleMap` instance for each thread. Within the JVM environment, a `ChronicleMap` instance is a `ConcurrentMap`, and could be accessed concurrently the same way as, for example, a `ConcurrentHashMap`. + +== Recovery + +If a process, accessing a persisted Chronicle Map, terminated abnormally, for example: + + - crashed + - `SIGKILL`ed + - terminated because the host operating system crashed + - terminated because the host machine lost power + +then the Chronicle Map may remain in an inaccessible or corrupted state. + +When the Chronicle Map is opened next time from another process, it should be done using `.recoverPersistedTo()` method in `ChronicleMapBuilder`. + +Unlike `createPersistedTo()`, this method scans all the memory of the Chronicle Map store for +inconsistencies and, if any are found, it cleans them up. + +`.recoverPersistedTo()` needs to access the Chronicle Map exclusively. If a concurrent process is +accessing the Chronicle Map while another process is attempting to perform recovery, the results of +operations on the accessing process side, and results of recovery are unspecified; the data could be corrupted further. You must ensure thst no other process is accessing the Chronicle Map store when +calling `.recoverPersistedTo()`. + +Example: + +```java +ChronicleMap cityPostalCodes = ChronicleMap + .of(CharSequence.class, PostalCodeRange.class) + .name("city-postal-codes-map") + .averageKey("Amsterdam") + .entries(50_000) + .recoverPersistedTo(cityPostalCodesFile, false); +``` + +The second parameter in `recoverPersistedTo()` method is called +`sameBuilderConfigAndLibraryVersion`. It has two possible values: + +- `true` - if `ChronicleMapBuilder` is configured +in exactly the same way, as when the Chronicle Map (persisted to the given file) was created, and +using the same version of the Chronicle Map library +- `false` - if the initial configuration is not +known, or the current version of Chronicle Map library could be different from the version originally used to +create this Chronicle Map. + +If `sameBuilderConfigAndLibraryVersion` is `true`, `recoverPersistedTo()` "knows" all the right +configurations, and what should be written to the header. It checks if the recovered Chronicle Map's +header memory (containing serialized configurations) is corrupted or not. If the header is +corrupted, it is overridden, and the recovery process continues. + +If `sameBuilderConfigAndLibraryVersion` is `false`, `recoverPersistedTo()` relies on the +configurations written to the Chronicle Map's header, assuming it is not corrupted. If it is +corrupted, `ChronicleHashRecoveryFailedException` is thrown. + +However, the subject header memory is never updated on ordinary operations with Chronicle Map, so it +couldn't be corrupted if an accessing process crashed, or the operating system crashed, or even the +machine lost power. Only hardware, memory, or disk corruption, or a bug in the file system, could lead +to Chronicle Map header memory corruption. + +`.recoverPersistedTo()` is harmless if the previous process accessing the Chronicle Map terminated +normally; however this is a computationally expensive procedure that should generally be avoided. + +Chronicle Map creation and recovery could be conveniently merged using a single call, `.createOrRecoverPersistedTo(persistenceFile, sameLibraryVersion)` in `ChronicleMapBuilder`. This acts like `createPersistedTo(persistenceFile)` if the persistence file doesn't yet exist, and like +`recoverPersistedTo(persistenceFile, sameLibraryVersion)`, if the file already exists. For example: + +```java +ChronicleMap cityPostalCodes = ChronicleMap + .of(CharSequence.class, PostalCodeRange.class) + .averageKey("Amsterdam") + .entries(50_000) + .createOrRecoverPersistedTo(cityPostalCodesFile, false); +``` + +If the Chronicle Map is configured to store entry checksums along with entries, then the recovery procedure checks that the checksum is correct for each entry. + +Otherwise, it assumes the entry is corrupted and deletes it from the Chronicle Map. If checksums are to be stored, the recovery procedure cannot guarantee +correctness of the entry data. See [Entry checksums](#entry-checksums) section for more information. + +== Key and Value Types + +The key, or value type, of `ChronicleMap` could be: + + - Types with best possible out-of-the-box support: + ** Any https://github.com/OpenHFT/Chronicle-Values[value interface] + ** Any class implementing http://openhft.github.io/Chronicle-Bytes/apidocs/net/openhft/chronicle/bytes/Byteable.html[`Byteable`] interface from https://github.com/OpenHFT/Chronicle-Bytes[Chronicle Bytes]. + ** Any class implementing http://openhft.github.io/Chronicle-Bytes/apidocs/net/openhft/chronicle/bytes/BytesMarshallable.html[`BytesMarshallable`]. + interface from Chronicle Bytes. The implementation class should have a public no-arg constructor. + ** `byte[]` and `ByteBuffer` + ** `CharSequence`, `String` and `StringBuilder`. Note that these char sequence types are + serialized using UTF-8 encoding by default. If you need a different encoding, refer to the + example in <>. + ** `Integer`, `Long` and `Double` + + - Types supported out-of-the-box, but that are not particularly efficiently. You may want to implement + more efficient <> for them: + ** Any class implementing `java.io.Externalizable`. The implementation class should have a public `no-arg` constructor. + ** Any type implementing `java.io.Serializable`, including boxed primitive types (except those listed + above) and array types. + + - Any other type, if <> are provided. + +https://github.com/OpenHFT/Chronicle-Values[Value interfaces] are preferred as they do not generate garbage, and have close to zero serialization/deserialization costs. They are preferable even to boxed primitives. For example, try to use `net.openhft.chronicle.core.values.IntValue` instead of `Integer`. + +Generally, you must provide hints for the `ChronicleMapBuilder` with the average sizes of the keys and the values, which are going to be inserted into the `ChronicleMap`. This is required in order to allocate the proper amount of shared memory. Do this using `averageKey()` (preferred) or `averageKeySize()`, and +`averageValue()` or `averageValueSize()` respectively. + +In 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 could be if values in your `ChronicleMap` are adjacency lists of some social graph, where nodes are represented as `long` identifiers, and adjacency lists are `long[]` arrays. For example, if the average number of friends is 150, you could configure the `ChronicleMap` as follows: + +```java +Map socialGraph = ChronicleMap + .of(Long.class, long[].class) + .name("social-graph-map") + .entries(1_000_000_000L) + .averageValue(new long[150]) + .create(); +``` + +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. + +If the key or value type is constantly sized, or keys or values only of a certain size appear in your Chronicle Map domain, then preferably you should configure `constantKeySizeBySample()` or +`constantValueSizeBySample()`, instead of `averageKey()` or `averageValue()`. For example: + +```java +ChronicleSet uuids = + ChronicleSet.of(UUID.class) + .name("uuids") + // All UUIDs take 16 bytes. + .constantKeySizeBySample(UUID.randomUUID()) + .entries(1_000_000) + .create(); +``` + +== Custom serializers + +Chronicle Map allows you to configure custom marshallers for key or value types which are not supported out-of-the-box. You can also serialize supported types like `String` in some custom way (encoded other than UTF-8), or serialize supported types more efficiently than by default. + +There are three pairs of serialization interfaces. Only one of them should be chosen in a single implementation, and supplied to the `ChronicleMapBuilder` for the key or value type. These are: + +- link:CM_Tutorial_Bytes.adoc[BytesWriter and BytesReader] +- link:CM_Tutorial_Sized.adoc[SizedWriter and SizedReader] +- link:CM_Tutorial_DataAccess.adoc[DataAccess and SizedReader] + +=== Custom serialization checklist + + 1. Choose the most suitable pair of serialization interfaces; link:CM_Tutorial_Bytes.adoc[BytesWriter and BytesReader], link:CM_Tutorial_Sized.adoc[SizedWriter and SizedReader], or link:CM_Tutorial_DataAccess.adoc[DataAccess and SizedReader]. Recommendations on which pair to choose are given in + the linked sections, describing each pair. + + 2. If implementation of the writer or reader part is configuration-less, give it a `private` + constructor, and define a single `INSTANCE` constant. A sole instance of this marshaller class in the JVM. Implement `ReadResolvable` and return `INSTANCE` from the `readResolve()` method. Do not make the implementation a Java `enum`. + + 3. If both the writer and reader are configuration-less, merge them into a single `-Marshaller` implementation class. + + 4. Make best efforts to reuse `using` objects on the reader side (`BytesReader` or `SizedReader`); including nesting objects. + + 5. Make best efforts to cache intermediate serialization results on writer side while working with some object. For example, try not to make expensive computations in both `size()` and `write()` methods + of the `SizedWriter` implementation. Rather, compute them and cache in an serializer instance + field. + + 6. Make best efforts to reuse intermediate objects that are used for reading or writing. Store them in instance fields of the serializer implementation. + + 7. If a serializer implementation is stateful, or has cache fields, implement `StatefulCopyable`. + + See link:CM_Tutorial_Understanding.adoc[Understanding `StatefulCopyable`] for more information. + + 8. Implement `writeMarshallable()` and `readMarshallable()` by writing and reading configuration fields (but not the state or cache fields) of the serializer instance one-by-one. Use the given + `WireOut`/`WireIn` object. + + See [Custom `CharSequence` encoding](#custom-charsequence-encoding) + section for some non-trivial example of implementing these methods. See also https://github.com/OpenHFT/Chronicle-Wire#using-wire[Wire tutorial]. + + 9. Don't forget to initialize transient/cache/state fileds of the instance in the end of + `readMarshallable()` implementation. This is needed, because fefore calling `readMarshallable()`, + Wire framework creates a serializer instance by means of `Unsafe.allocateInstance()` rather than + calling any constructor. + + 10. If implementing `DataAccess`, consider implementation to be `Data` also, and return `this` from + `getData()` method. + + 11. Don't forget to implement `equals()`, `hashCode()` and `toString()` in `Data` implementation, + returned from `DataAccess.getData()` method, regardless if this is actually the same `DataAccess` + object, or a separate object. + + 12. Except `DataAccess` which is also a `Data`, serializers shouldn't override Object's `equals()`, + `hashCode()` and `toString()` (these methods are never called on serializers inside Chronicle Map + library); they shouldn't implement `Serializable` or `Externalizable` (but have to implement + `net.openhft.chronicle.wire.Marshallable`); shouldn't implement `Cloneable` (but have to implement + `StatefulCopyable`, if they are stateful or have cache fields). + + 13. After implementing custom serializers, don't forget to actually apply them to + `ChronicleMapBuilder` by `keyMarshallers()`, `keyReaderAndDataAccess()`, `valueMarshallers()` or + `valueReaderAndDataAccess()` methods. + +== `ChronicleMap` usage patterns + +=== Single-key queries + +`ChronicleMap` supports all operations from: + + - https://docs.oracle.com/javase/8/docs/api/java/util/Map.html[`Map`] interfaces; `get()`, `put()`, etc, including +methods added in Java 8, like `compute()` and `merge()`, and + - https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html[`ConcurrentMap`] interfaces; +`putIfAbsent()`, `replace()`. + +All operations, including those which include "two-steps", for example, `compute()`, are correctly synchronized in terms of the `ConcurrentMap` interface. This means that you could use a `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"); +``` + +However, this approach often generates garbage, because the values should be deserialized from +off-heap memory to on-heap memory when the new value objects are allocated. There are several possibilities to +reuse objects efficiently: + +==== Value interfaces instead of boxed primitives + +If you want to create a `ChronicleMap` where keys are `long` ids, use `LongValue` instead of `Long` +key: + +```java +ChronicleMap orders = ChronicleMap + .of(LongValue.class, Order.class) + .name("orders-map") + .entries(1_000_000) + .create(); + +LongValue key = Values.newHeapInstance(LongValue.class); +key.setValue(id); +orders.put(key, order); + +... + +long[] orderIds = ... +// Allocate a single heap instance for inserting all keys from the array. +// This could be a cached or ThreadLocal value as well, eliminating +// allocations altogether. +LongValue key = Values.newHeapInstance(LongValue.class); +for (long id : orderIds) { + // Reuse the heap instance for each key + key.setValue(id); + Order order = orders.get(key); + // process the order... +} +``` + +==== `chronicleMap.getUsing()` + +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)); + ``` + +The difference is that it doesn't generate garbage. The value type is the value interface. Pass the 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`, then `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()`, + then `ChronicleMap` attempts to reuse the given object by calling the `readUsing()` method from the + marshaller interface. + +If `ChronicleMap` fails to reuse the object in `getUsing()`, it does no harm. It falls back to +object creation, as in the `get()` method. In particular, even `null` is allowed to be passed as +`using` object. It allows a "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: If the value type is a value interface, **do not** use flyweight implementation as the `getUsing()` argument. This is dangerous, because on reusing flyweight points to the `ChronicleMap` memory +directly, but the access is not synchronized. At best you could read inconsistent value state; +at worst you could corrupt the `ChronicleMap` memory. + +For accessing the `ChronicleMap` value memory directly use the following techniques. + +==== Working with an entry within a context + +```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 + +In this example, consistent graph edge addition and removals are implemented using 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); + } +} + +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) + .name("graph") + .entries(100) + .averageValue(averageValue) + .create(); + +addEdge(graph, 1, 2); +removeEdge(graph, 1, 2); +``` + +== Close `ChronicleMap` +Unlike `ConcurrentHashMap`, `ChronicleMap` stores its data off-heap; often in a memory mapped file. +It is recommended that you call `close()` when you have finished working with a `ChronicleMap`. + +``` java +map.close() +``` + +This is especially important when working with Chronicle Map replication, as failure to call close may prevent +you from restarting a replicated map on the same port. + +In the event that your application crashes, it may not +be possible to call `close()`. Your operating system will usually close dangling ports automatically. So, although it is recommended that you `close()` when you have finished with the map, +it is not something that you must do; it's just something that we recommend you should do. + +WARNING: If you call `close()` too early before you have finished working with the map, this can cause +your JVM to crash. Close **MUST** be the last thing that you do with the map. + +== Behaviour Customization + +You can customize the behaviour of Chronicle Map. + +See <> for more details. + + +== Entry checksums + +Chronicle Map is able to store entry checksums along with entries. With entry checksums it is +possible to identify partially written entries (in the case of operating system, or power failure), +and corrupted entries (in the case of hardware, memory, or disk corruption) and clean them up during the <> procedure. + +Entry checksums are `32-bit` numbers, computed by a hash function with good avalanche effect. +Theoretically, there is still about a one-in-a-billion chance that after entry corruption, it passes the sum check. + +By default, entry checksums are: + + - **`ON`** if the Chronicle Map is persisted to disk (i. e. created via +`createPersistedTo()` method) +- **`OFF`** if the Chronicle Map is purely in-memory. + +Storing checksums for a purely in-memory Chronicle Map hardly makes any practical sense, but you might want to disable storing checksums for a persisted Chronicle Map by calling `.checksumEntries(false)` on the `ChronicleMapBuilder` used to create a map. It makes sense if you don't need extra safety that checksums provide. + +Entry checksums are computed automatically when an entry is inserted into a Chronicle Map, and +re-computed automatically on operations which update the whole value. For example, `map.put()`, +`map.replace()`, `map.compute()`, `mapEntry.doReplaceValue()`. See the `MapEntry` interface in +http://www.javadoc.io/doc/net.openhft/chronicle-map/[Javadocs]. If you update values directly, bypassing Chronicle Map logic, keeping the entry checksum up-to-date is also your responsibility. + +It is strongly recommended to update off-heap memory of values directly only within a context, and update or write lock held. Within a context, you are provided with an entry object of `MapEntry` type. To re-compute entry checksum manually, cast that object to `ChecksumEntry` type and call the `.updateChecksum()` method: + +``` java +try (ChronicleMap map = ChronicleMap + .of(Integer.class, LongValue.class) + .entries(1) + // Entry checksums make sense only for persisted Chronicle Maps, and are ON by + // default for such maps + .createPersistedTo(file)) { + + LongValue value = Values.newHeapInstance(LongValue.class); + value.setValue(42); + map.put(1, value); + + try (ExternalMapQueryContext c = map.queryContext(1)) { + // Update lock required for calling ChecksumEntry.checkSum() + c.updateLock().lock(); + MapEntry entry = c.entry(); + Assert.assertNotNull(entry); + ChecksumEntry checksumEntry = (ChecksumEntry) entry; + Assert.assertTrue(checksumEntry.checkSum()); + + // to access off-heap bytes, should call value().getUsing() with Native value + // provided. Simple get() return Heap value by default + LongValue nativeValue = + entry.value().getUsing(Values.newNativeReference(LongValue.class)); + // This value bytes update bypass Chronicle Map internals, so checksum is not + // updated automatically + nativeValue.setValue(43); + Assert.assertFalse(checksumEntry.checkSum()); + + // Restore correct checksum + checksumEntry.updateChecksum(); + Assert.assertTrue(checksumEntry.checkSum()); + } +} +``` + +''' +<<../ReadMe.adoc#,Back to ReadMe>> diff --git a/docs/CM_Tutorial_Behaviour.adoc b/docs/CM_Tutorial_Behaviour.adoc new file mode 100755 index 000000000..4d01f5d21 --- /dev/null +++ b/docs/CM_Tutorial_Behaviour.adoc @@ -0,0 +1,342 @@ += Behaviour Customization +Neil Clifford +:toc: macro +:toclevels: 2 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +== Customization + +You could customize `ChronicleMap` behaviour on several levels: + + - `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 using this configured SPI. + + - `ChronicleMapBuilder.mapMethods()` is the higher-level of listening for local calls of Map + methods. Methods in the `MapMethods` interface correspond to `Map` interface methods with the same + names, and define their implementations for `ChronicleMap`. + + - `ChronicleMapBuilder.remoteOperations()` is for listening and customizing behaviour of remote + calls, and replication events. + +All executions around `ChronicleMap` go through all three tiers, or just the last two): + + 1. Query tier: `MapQueryContext` interface + 2. Entry tier: `MapEntry` and `MapAbsentEntry` interfaces + 3. Data tier: `Data` interface + +`MapMethods` and `MapRemoteOperations` methods accept *query context*; SPI is above +the Query tier. + +`MapEntryOperations` methods accept `MapEntry` or `MapAbsentEntry`; SPI is between Query and Entry tiers. + +When combined, interception SPI interfaces and the `ChronicleMap.queryContext()` API are powerful enough to: + + - Log all operations of some kind on `ChronicleMap` (for example, all remove, insert, or update operations). + - Log some specific operations on `ChronicleMap` (for example, log only `acquireUsing()` calls, which has created a new entry). + - Inhibit operations of some kind on the `ChronicleMap` instance. + - Backup all changes to `ChronicleMap` to some alternative storage; for example, an 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 a concurrent environment, by acquiring locks on all keys before updating the entries. + - Define your own replication/reconciliation logic for distributed Chronicle Maps. + - Dump statistics of the Chronicle Map instance; each segment's load, size in bytes of each + entry, etc. + +== Example - Simple logging + +To log all modification operations on `ChronicleMap`: + +```java +class SimpleLoggingMapEntryOperations implements MapEntryOperations { + + private static final SimpleLoggingMapEntryOperations INSTANCE = + new SimpleLoggingMapEntryOperations(); + + public static MapEntryOperations simpleLoggingMapEntryOperations() { + return SimpleLoggingMapEntryOperations.INSTANCE; + } + + private SimpleLoggingMapEntryOperations() {} + + @Override + public Void remove(@NotNull MapEntry entry) { + System.out.println("remove " + entry.key() + ": " + entry.value()); + entry.doRemove(); + return null; + } + + @Override + public Void replaceValue(@NotNull MapEntry entry, Data newValue) { + System.out.println("replace " + entry.key() + ": " + entry.value() + " -> " + newValue); + entry.doReplaceValue(newValue); + return null; + } + + @Override + public Void insert(@NotNull MapAbsentEntry absentEntry, Data value) { + System.out.println("insert " + absentEntry.absentKey() + " -> " + value); + absentEntry.doInsert(value); + return null; + } +} +``` + +Usage: + +```java +ChronicleMap map = ChronicleMap + .of(Integer.class, IntValue.class) + .entries(100) + .entryOperations(simpleLoggingMapEntryOperations()) + .create(); + +// do anything with the map +``` + +== Example - BiMap + +Possible bi-directional map (that is, a map that preserves the uniqueness of its values, as well +as that of its keys) implementation over Chronicle Maps. + +```java +enum DualLockSuccess {SUCCESS, FAIL} +``` + +```java +class BiMapMethods implements MapMethods { + @Override + public void remove(MapQueryContext q, ReturnValue returnValue) { + while (true) { + q.updateLock().lock(); + try { + MapEntry entry = q.entry(); + if (entry != null) { + returnValue.returnValue(entry.value()); + if (q.remove(entry) == SUCCESS) + return; + } + } finally { + q.readLock().unlock(); + } + } + } + + @Override + public void put(MapQueryContext q, Data value, + ReturnValue returnValue) { + while (true) { + q.updateLock().lock(); + try { + MapEntry entry = q.entry(); + if (entry != null) { + throw new IllegalStateException(); + } else { + if (q.insert(q.absentEntry(), value) == SUCCESS) + return; + } + } finally { + q.readLock().unlock(); + } + } + } + + @Override + public void putIfAbsent(MapQueryContext q, Data value, + ReturnValue returnValue) { + while (true) { + try { + if (q.readLock().tryLock()) { + MapEntry entry = q.entry(); + if (entry != null) { + returnValue.returnValue(entry.value()); + return; + } + // Key is absent + q.readLock().unlock(); + } + q.updateLock().lock(); + MapEntry entry = q.entry(); + if (entry != null) { + returnValue.returnValue(entry.value()); + return; + } + // Key is absent + if (q.insert(q.absentEntry(), value) == SUCCESS) + return; + } finally { + q.readLock().unlock(); + } + } + } + + @Override + public boolean remove(MapQueryContext q, Data value) { + while (true) { + q.updateLock().lock(); + MapEntry entry = q.entry(); + try { + if (entry != null && bytesEquivalent(entry.value(), value)) { + if (q.remove(entry) == SUCCESS) { + return true; + } else { + //noinspection UnnecessaryContinue + continue; + } + } else { + return false; + } + } finally { + q.readLock().unlock(); + } + } + } + + @Override + public void acquireUsing(MapQueryContext q, + ReturnValue returnValue) { + throw new UnsupportedOperationException(); + } + + @Override + public void replace(MapQueryContext q, Data value, + ReturnValue returnValue) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean replace(MapQueryContext q, Data oldValue, + Data newValue) { + throw new UnsupportedOperationException(); + } + + @Override + public void compute(MapQueryContext q, + BiFunction remappingFunction, + ReturnValue returnValue) { + throw new UnsupportedOperationException(); + } + + @Override + public void merge(MapQueryContext q, Data value, + BiFunction remappingFunction, + ReturnValue returnValue) { + throw new UnsupportedOperationException(); + } +} +``` + +```java +class BiMapEntryOperations implements MapEntryOperations { + ChronicleMap reverse; + + public void setReverse(ChronicleMap reverse) { + this.reverse = reverse; + } + + @Override + public DualLockSuccess remove(@NotNull MapEntry entry) { + try (ExternalMapQueryContext rq = reverse.queryContext(entry.value())) { + if (!rq.updateLock().tryLock()) { + if (entry.context() instanceof MapQueryContext) + return FAIL; + throw new IllegalStateException("Concurrent modifications to reverse map " + + "during remove during iteration"); + } + MapEntry reverseEntry = rq.entry(); + if (reverseEntry != null) { + entry.doRemove(); + reverseEntry.doRemove(); + return SUCCESS; + } else { + throw new IllegalStateException(entry.key() + " maps to " + entry.value() + + ", but in the reverse map this value is absent"); + } + } + } + + @Override + public DualLockSuccess replaceValue(@NotNull MapEntry entry, Data newValue) { + throw new UnsupportedOperationException(); + } + + @Override + public DualLockSuccess insert(@NotNull MapAbsentEntry absentEntry, + Data value) { + try (ExternalMapQueryContext rq = reverse.queryContext(value)) { + if (!rq.updateLock().tryLock()) + return FAIL; + MapAbsentEntry reverseAbsentEntry = rq.absentEntry(); + if (reverseAbsentEntry != null) { + absentEntry.doInsert(value); + reverseAbsentEntry.doInsert(absentEntry.absentKey()); + return SUCCESS; + } else { + Data reverseKey = rq.entry().value(); + if (reverseKey.equals(absentEntry.absentKey())) { + // recover + absentEntry.doInsert(value); + return SUCCESS; + } + throw new IllegalArgumentException("Try to associate " + + absentEntry.absentKey() + " with " + value + ", but in the reverse " + + "map this value already maps to " + reverseKey); + } + } + } +} +``` + +Usage: + +```java +BiMapEntryOperations biMapOps1 = new BiMapEntryOperations<>(); +ChronicleMap map1 = ChronicleMapBuilder + .of(Integer.class, CharSequence.class) + .name("direct-bimap") + .entries(100) + .actualSegments(1) + .averageValueSize(10) + .entryOperations(biMapOps1) + .mapMethods(new BiMapMethods<>()) + .create(); + +BiMapEntryOperations biMapOps2 = new BiMapEntryOperations<>(); +ChronicleMap map2 = ChronicleMapBuilder + .of(CharSequence.class, Integer.class) + .name("reverse-bimap") + .entries(100) + .actualSegments(1) + .averageKeySize(10) + .entryOperations(biMapOps2) + .mapMethods(new BiMapMethods<>()) + .create(); + +biMapOps1.setReverse(map2); +biMapOps2.setReverse(map1); + +map1.put(1, "1"); +System.out.println(map2.get("1")); +``` + +== Example - Monitor Chronicle Map Statistics + +```java + public static void printMapStats(ChronicleMap map) { + for (int i = 0; i < map.segments(); i++) { + try (MapSegmentContext c = map.segmentContext(i)) { + System.out.printf("segment %d contains %d entries\n", i, c.size()); + c.forEachSegmentEntry(e -> System.out.printf("%s, %d bytes -> %s, %d bytes\n", + e.key(), e.key().size(), e.value(), e.value().size())); + } + } + } +``` + +''' +<> + diff --git a/docs/CM_Tutorial_Bytes.adoc b/docs/CM_Tutorial_Bytes.adoc new file mode 100755 index 000000000..feec46569 --- /dev/null +++ b/docs/CM_Tutorial_Bytes.adoc @@ -0,0 +1,404 @@ += `BytesWriter` and `BytesReader` +Neil Clifford +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +This pair of interfaces is configured using `ChronicleMapBuilder.keyMarshallers()` or +`valueMarshallers()`, for the key, or value type of the map, respectively. + +These interfaces are the most suitable, where the size of the serialized form is not known in advance. The easiest way to compute the size of the serialized form of some object of the +type, is performing serialization itself, and then looking at the number of written bytes. + +This interface-pair is the least efficient, and the simplest to implement, so it should also be +used when efficiency is not the top priority, or when gains of using other pairs of interfaces +(which are more complicated to implement) are marginal. + +Basically you should implement two serialization methods: + + - `void write(Bytes out, @NotNull T toWrite)` from `BytesWriter` interface, which writes the given + `toWrite` instance of the serialized type to the given `out` bytes sink. + - `T read(Bytes in, @Nullable T using)` from `BytesReader` interface, which reads the serialized + object into the given `using` instance (if the serialized type is reusable, the `using` object is + not `null`, and suitable for reusing for this particular serialized object), or a newly created + instance. The returned object contains the serialized data; it may be identical, or not identical, to the passed `using` instance. + +For example, here is the implementation of `BytesWriter` and `BytesReader` for `CharSequence[]` +value type (array of CharSequences): + +```java +public final class CharSequenceArrayBytesMarshaller + implements BytesWriter, BytesReader, + ReadResolvable { + + static final CharSequenceArrayBytesMarshaller INSTANCE = new CharSequenceArrayBytesMarshaller(); + + private CharSequenceArrayBytesMarshaller() {} + + @Override + public void write(Bytes out, @NotNull CharSequence[] toWrite) { + out.writeInt(toWrite.length); + for (CharSequence cs : toWrite) { + // Assume elements non-null for simplicity + Objects.requireNonNull(cs); + out.writeUtf8(cs); + } + } + + @NotNull + @Override + public CharSequence[] read(Bytes in, @Nullable CharSequence[] using) { + int len = in.readInt(); + if (using == null) + using = new CharSequence[len]; + if (using.length != len) + using = Arrays.copyOf(using, len); + for (int i = 0; i < len; i++) { + CharSequence cs = using[i]; + if (cs instanceof StringBuilder) { + in.readUtf8((StringBuilder) cs); + } else { + StringBuilder sb = new StringBuilder(0); + in.readUtf8(sb); + using[i] = sb; + } + } + return using; + } + + @Override + public void writeMarshallable(@NotNull WireOut wireOut) { + // no fields to write + } + + @Override + public void readMarshallable(@NotNull WireIn wireIn) { + // no fields to read + } + + @Override + public CharSequenceArrayBytesMarshaller readResolve() { + return INSTANCE; + } +} +``` + +Usage example: + +```java +try (ChronicleMap map = ChronicleMap + .of(String.class, CharSequence[].class) + .averageKey("fruits") + .valueMarshaller(CharSequenceArrayBytesMarshaller.INSTANCE) + .averageValue(new CharSequence[]{"banana", "pineapple"}) + .entries(2) + .create()) { + map.put("fruits", new CharSequence[]{"banana", "pineapple"}); + map.put("vegetables", new CharSequence[] {"carrot", "potato"}); + Assert.assertEquals(2, map.get("fruits").length); + Assert.assertEquals(2, map.get("vegetables").length); +} +``` + +The total size of serialization-form for a `CharSequence[]` array is 4 bytes for storing the +array length, plus the sum of sizes of all CharSequences, in UTF-8 encoding. Computing this size +without actual encoding has comparable computational cost with performing actual encoding. That +makes `CharSequence[]` type to meet the second criteria (see above). This makes `BytesWriter` and +`BytesReader` the most suitable pair of serialization interfaces to implement for the type. + +Note how `read()` implementation attempts to reuse, not only the array object, but also the elements, +minimizing the amount of produced garbage. This is a recommended practice. + +Some additional notes: + + - If the reader, or writer, interface implementation is not configurable, and doesn't have + per-instance cache or state fields (it doesn't have instance fields at all), there is + a convention to make such implementation classes `final`, give them a `private` constructor, and then + expose a single `INSTANCE` constant; a sole instance of this implementation in the JVM. + - *Do not* make `marshaller` class `enum`, because there are some issues with `enum` serialization/deserialization. + - For such no-state implementations, do not forget to implement the `ReadResolvable` interface and return `INSTANCE`. Otherwise you have no guarantee that `INSTANCE` constant is the only alive instance of this implementation in the JVM. + - If *both* the writer and reader interface implementations have no fields, it might be a good idea + to merge them into a single type, in order to keep writing and reading logic together. + +=== Custom `CharSequence` encoding + +Another example shows how to serialize `CharSequence`s using custom encoding (rather than UTF-8): + +Writer: + +```java +public final class CharSequenceCustomEncodingBytesWriter + implements BytesWriter, + StatefulCopyable { + + // config fields, non-final because read in readMarshallable() + private Charset charset; + private int inputBufferSize; + + // cache fields + private transient CharsetEncoder charsetEncoder; + private transient CharBuffer inputBuffer; + private transient ByteBuffer outputBuffer; + + public CharSequenceCustomEncodingBytesWriter(Charset charset, int inputBufferSize) { + this.charset = charset; + this.inputBufferSize = inputBufferSize; + initTransients(); + } + + private void initTransients() { + charsetEncoder = charset.newEncoder(); + inputBuffer = CharBuffer.allocate(inputBufferSize); + int outputBufferSize = (int) (inputBufferSize * charsetEncoder.averageBytesPerChar()); + outputBuffer = ByteBuffer.allocate(outputBufferSize); + } + + @Override + public void write(Bytes out, @NotNull CharSequence cs) { + // Write the actual cs length for accurate StringBuilder.ensureCapacity() while reading + out.writeStopBit(cs.length()); + long encodedSizePos = out.writePosition(); + out.writeSkip(4); + charsetEncoder.reset(); + inputBuffer.clear(); + outputBuffer.clear(); + int csPos = 0; + boolean endOfInput = false; + // this loop inspired by the CharsetEncoder.encode(CharBuffer) implementation + while (true) { + if (!endOfInput) { + int nextCsPos = Math.min(csPos + inputBuffer.remaining(), cs.length()); + append(inputBuffer, cs, csPos, nextCsPos); + inputBuffer.flip(); + endOfInput = nextCsPos == cs.length(); + csPos = nextCsPos; + } + + CoderResult cr = inputBuffer.hasRemaining() ? + charsetEncoder.encode(inputBuffer, outputBuffer, endOfInput) : + CoderResult.UNDERFLOW; + + if (cr.isUnderflow() && endOfInput) + cr = charsetEncoder.flush(outputBuffer); + + if (cr.isUnderflow()) { + if (endOfInput) { + break; + } else { + inputBuffer.compact(); + continue; + } + } + + if (cr.isOverflow()) { + outputBuffer.flip(); + writeOutputBuffer(out); + outputBuffer.clear(); + continue; + } + + try { + cr.throwException(); + } catch (CharacterCodingException e) { + throw new IORuntimeException(e); + } + } + outputBuffer.flip(); + writeOutputBuffer(out); + + out.writeInt(encodedSizePos, (int) (out.writePosition() - encodedSizePos - 4)); + } + + private void writeOutputBuffer(Bytes out) { + int remaining = outputBuffer.remaining(); + out.write(out.writePosition(), outputBuffer, 0, remaining); + out.writeSkip(remaining); + } + + /** + * Need this method because {@link CharBuffer#append(CharSequence, int, int)} produces garbage + */ + private static void append(CharBuffer charBuffer, CharSequence cs, int start, int end) { + for (int i = start; i < end; i++) { + charBuffer.put(cs.charAt(i)); + } + } + + @Override + public void readMarshallable(@NotNull WireIn wireIn) { + charset = (Charset) wireIn.read(() -> "charset").object(); + inputBufferSize = wireIn.read(() -> "inputBufferSize").int32(); + initTransients(); + } + + @Override + public void writeMarshallable(@NotNull WireOut wireOut) { + wireOut.write(() -> "charset").object(charset); + wireOut.write(() -> "inputBufferSize").int32(inputBufferSize); + } + + @Override + public CharSequenceCustomEncodingBytesWriter copy() { + return new CharSequenceCustomEncodingBytesWriter(charset, inputBufferSize); + } +} +``` + +Reader: + +```java +public final class CharSequenceCustomEncodingBytesReader + implements BytesReader, + StatefulCopyable { + + // config fields, non-final because read in readMarshallable() + private Charset charset; + private int inputBufferSize; + + // cache fields + private transient CharsetDecoder charsetDecoder; + private transient ByteBuffer inputBuffer; + private transient CharBuffer outputBuffer; + + public CharSequenceCustomEncodingBytesReader(Charset charset, int inputBufferSize) { + this.charset = charset; + this.inputBufferSize = inputBufferSize; + initTransients(); + } + + private void initTransients() { + charsetDecoder = charset.newDecoder(); + inputBuffer = ByteBuffer.allocate(inputBufferSize); + int outputBufferSize = (int) (inputBufferSize * charsetDecoder.averageCharsPerByte()); + outputBuffer = CharBuffer.allocate(outputBufferSize); + } + + @NotNull + @Override + public CharSequence read(Bytes in, @Nullable CharSequence using) { + long csLengthAsLong = in.readStopBit(); + if (csLengthAsLong > Integer.MAX_VALUE) { + throw new IORuntimeException("cs len shouldn't be more than " + Integer.MAX_VALUE + + ", " + csLengthAsLong + " read"); + } + int csLength = (int) csLengthAsLong; + StringBuilder sb; + if (using instanceof StringBuilder) { + sb = (StringBuilder) using; + sb.setLength(0); + sb.ensureCapacity(csLength); + } else { + sb = new StringBuilder(csLength); + } + + int remainingBytes = in.readInt(); + charsetDecoder.reset(); + inputBuffer.clear(); + outputBuffer.clear(); + boolean endOfInput = false; + // this loop inspired by the CharsetDecoder.decode(ByteBuffer) implementation + while (true) { + if (!endOfInput) { + int inputChunkSize = Math.min(inputBuffer.remaining(), remainingBytes); + inputBuffer.limit(inputBuffer.position() + inputChunkSize); + in.read(inputBuffer); + inputBuffer.flip(); + remainingBytes -= inputChunkSize; + endOfInput = remainingBytes == 0; + } + + CoderResult cr = inputBuffer.hasRemaining() ? + charsetDecoder.decode(inputBuffer, outputBuffer, endOfInput) : + CoderResult.UNDERFLOW; + + if (cr.isUnderflow() && endOfInput) + cr = charsetDecoder.flush(outputBuffer); + + if (cr.isUnderflow()) { + if (endOfInput) { + break; + } else { + inputBuffer.compact(); + continue; + } + } + + if (cr.isOverflow()) { + outputBuffer.flip(); + sb.append(outputBuffer); + outputBuffer.clear(); + continue; + } + + try { + cr.throwException(); + } catch (CharacterCodingException e) { + throw new IORuntimeException(e); + } + } + outputBuffer.flip(); + sb.append(outputBuffer); + + return sb; + } + + @Override + public void readMarshallable(@NotNull WireIn wireIn) throws IORuntimeException { + charset = (Charset) wireIn.read(() -> "charset").object(); + inputBufferSize = wireIn.read(() -> "inputBufferSize").int32(); + initTransients(); + } + + @Override + public void writeMarshallable(@NotNull WireOut wireOut) { + wireOut.write(() -> "charset").object(charset); + wireOut.write(() -> "inputBufferSize").int32(inputBufferSize); + } + + @Override + public CharSequenceCustomEncodingBytesReader copy() { + return new CharSequenceCustomEncodingBytesReader(charset, inputBufferSize); + } +} +``` + +Usage example: + +```java +Charset charset = Charset.forName("GBK"); +int charBufferSize = 100; +int bytesBufferSize = 200; +CharSequenceCustomEncodingBytesWriter writer = + new CharSequenceCustomEncodingBytesWriter(charset, charBufferSize); +CharSequenceCustomEncodingBytesReader reader = + new CharSequenceCustomEncodingBytesReader(charset, bytesBufferSize); +try (ChronicleMap englishToChinese = ChronicleMap + .of(String.class, CharSequence.class) + .valueMarshallers(reader, writer) + .averageKey("hello") + .averageValue("你好") + .entries(10) + .create()) { + englishToChinese.put("hello", "你好"); + englishToChinese.put("bye", "再见"); + + Assert.assertEquals("你好", englishToChinese.get("hello").toString()); + Assert.assertEquals("再见", englishToChinese.get("bye").toString()); +} +``` + +Some notes on this form of custom serialization: + + - Both `CharSequenceCustomEncodingBytesWriter` and `CharSequenceCustomEncodingBytesReader` have + configurations (charset and input buffer size). They are implemented as normal classes rather + than classes with `private` constructors and a single `INSTANCE`. + - Both writer and reader classes have some "cache" fields; their contents are mutated during + writing and reading. That is why they have to implement the `StatefulCopyable` interface. + + +''' +<> diff --git a/docs/CM_Tutorial_DataAccess.adoc b/docs/CM_Tutorial_DataAccess.adoc new file mode 100755 index 000000000..2ed00f063 --- /dev/null +++ b/docs/CM_Tutorial_DataAccess.adoc @@ -0,0 +1,164 @@ += `DataAccess` and `SizedReader` +Neil Clifford +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +This pair of interfaces is configured using `ChronicleMapBuilder.keyReaderAndDataAccess()` or +`valueReaderAndDataAccess()` for the key, or value type, of the map respectively. + +The reader part, `SizedReader`, is the same as in <> pair. `DataAccess` is an "advanced" interface to replace `SizedWriter`. + +The main method in `DataAccess` is `Data getData(@NotNull T instance)`. It returns a `Data` +accessor which is used to write a "serialized" form of the instance to off-heap memory. `Data.size()` +on the returned `Data` object is used for the same purpose as the `SizedWriter.size()` method in +the <> pair interfaces. `Data.writeTo()` +is used instead of `SizedWriter.write()`. + +`DataAccess` assumes that the `Data` object, returned from the `getData()` method is cached in some way. That is why it also has an `uninit()` method to clear references to the serialized object after a query operation to a Chronicle Map is complete to prevent memory leaks. This, in turn, implies that the `DataAccess` implementation is stateful. Therefore, `DataAccess` is made a sub-interface of +`StatefulCopyable` to force all `DataAccess` implementations to implement `StatefulCopyable` as well. + +See <> for more infromation +on this. + +If your `DataAccess` implementation is not actually stateful, it is free to return `this` from the `StatefulCopyable.copy()` method. + +The `DataAccess` interface is primarily intended for "serializing" objects that are already sequences of bytes, and in fact do not require serialization; for example, `byte[]`, `ByteBuffer`, arrays of Java +primitives. For such types of objects, `DataAccess` allows bypassing of the intermediate buffering, copying data directly from objects to Chronicle Map's off-heap memory. + +For example, look at the `DataAccess` implementation for `byte[]`: + +```java +public final class ByteArrayDataAccess extends AbstractData implements DataAccess { + + /** Cache field */ + private transient HeapBytesStore bs; + + /** State field */ + private transient byte[] array; + + public ByteArrayDataAccess() { + initTransients(); + } + + private void initTransients() { + bs = HeapBytesStore.uninitialized(); + } + + @Override + public RandomDataInput bytes() { + return bs; + } + + @Override + public long offset() { + return bs.start(); + } + + @Override + public long size() { + return bs.capacity(); + } + + @Override + public byte[] get() { + return array; + } + + @Override + public byte[] getUsing(@Nullable byte[] using) { + if (using == null || using.length != array.length) + using = new byte[array.length]; + System.arraycopy(array, 0, using, 0, array.length); + return using; + } + + @Override + public Data getData(@NotNull byte[] instance) { + array = instance; + bs.init(instance); + return this; + } + + @Override + public void uninit() { + array = null; + bs.uninit(); + } + + @Override + public DataAccess copy() { + return new ByteArrayDataAccess(); + } + + @Override + public void writeMarshallable(@NotNull WireOut wireOut) { + // no fields to write + } + + @Override + public void readMarshallable(@NotNull WireIn wireIn) { + // no fields to read + initTransients(); + } +} +``` + +The `getData()` method returns `this`, and the `DataAccess` implementation implements the `Data` interface as well. This is recommended practice, because it reduces the number of objects involved (hence pointer chasing), and keeps `DataAccess`, and `Data` logic together. + +The `Data` interface puts constraints on `equals()`, `hashCode()`, and `toString()` implementations. This is why `ByteArrayDataAccess` sub-class'es `AbstractData`, and inherits proper implementations from it. +A serializer strategy implementation can have `equals()`, `hashCode()`, and `toString()` from a very different domain, because those methods are never called on serializers inside Chronicle Map. + +The easiest way to implement `equals()`, `hashCode()`, and `toString()` is to extend the `AbstractData` class. If it is not possible (perhaps the `Data` implementation already extends some other class), do this +by delegating to `dataEquals()`, `dataHashCode()`, and `dataToString()` default methods, provided in the `Data` interface. + +Corresponding `SizedReader` for `byte[]`: + +```java +public final class ByteArraySizedReader + implements SizedReader, Marshallable, ReadResolvable { + + public static final ByteArraySizedReader INSTANCE = new ByteArraySizedReader(); + + private ByteArraySizedReader() {} + + @NotNull + @Override + public byte[] read(@NotNull Bytes in, long size, @Nullable byte[] using) { + if (size < 0L || size > (long) Integer.MAX_VALUE) { + throw new IORuntimeException("byte[] size should be non-negative int, " + + size + " given. Memory corruption?"); + } + int arrayLength = (int) size; + if (using == null || arrayLength != using.length) + using = new byte[arrayLength]; + in.read(using); + return using; + } + + @Override + public void writeMarshallable(@NotNull WireOut wireOut) { + // no fields to write + } + + @Override + public void readMarshallable(@NotNull WireIn wireIn) { + // no fields to read + } + + @Override + public ByteArraySizedReader readResolve() { + return INSTANCE; + } +} +``` + +NOTE: If you configure `byte[]` key, or value type, then this pair of serializers is used as the default. + + +''' +<> \ No newline at end of file diff --git a/docs/CM_Tutorial_Sized.adoc b/docs/CM_Tutorial_Sized.adoc new file mode 100755 index 000000000..1fc2534d2 --- /dev/null +++ b/docs/CM_Tutorial_Sized.adoc @@ -0,0 +1,156 @@ += `SizedWriter` and `SizedReader` +Neil Clifford +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +This pair of interfaces is configured using `ChronicleMapBuilder.keyMarshallers()` or +`valueMarshallers()` for the key, or value type, of the map, respectively. Overloaded methods; those +for [`BytesWriter` and `BytesReader`](#byteswriter-and-bytesreader) interfaces have the same names). + +The main two methods to implement in `SizedWriter` interface are: + + - `long size(@NotNull T toWrite);` returns the number of bytes, which is written by the subsequent + `write()` method given the same `toWrite` instance of the serialized type. + - `void write(Bytes out, long size, @NotNull T toWrite);` writes the given `toWrite` instance to + the given `out` bytes sink. Additionally, `size` is provided, which is the value computed for the + same `toWrite` instance by calling `size()` method. When `SizedWriter` is used internally, + the `size()` method is always called before `write()`; caching logic in `SizedWriter` implementation + may rely on this. This method should advance the `writePosition()` of the given out `Bytes` exactly by + the given `size` number of bytes. + +`SizedReader`'s `T read(Bytes in, long size, @Nullable T using);` method is similar to the +corresponding `read()` method in `BytesReader` interface, except that the size of the serialized object is provided. + +This pair of interfaces is suitable, if: + + - The serialized form of the type effectively includes the size of the rest of the serialized form + in the beginning. + + - The type is not a plain sequence of bytes; for example, `byte[]` or `ByteBuffer`. For `byte[]` or `ByteBuffer`, the <> pair of interfaces is a much better + choice. + +Examples of such types include lists, and arrays of constant-sized elements. + +Compared to `BytesWriter` and `BytesReader`, this pair of interfaces allows the sharing of information +about the serialized form, between serialization logic, and the Chronicle Map; this saves a few bytes by storing the serialization size only once, rather than twice. This pair of interfaces is not much more difficult to implement, but the gains are also not large. + +Example: Serializing lists of simple `Point` structures: + +```java +public final class Point { + + public static Point of(double x, double y) { + Point p = new Point(); + p.x = x; + p.y = y; + return p; + } + + double x, y; +} +``` + +Serializer implementation: + +```java +public final class PointListSizedMarshaller + implements SizedReader>, SizedWriter>, + ReadResolvable { + + static final PointListSizedMarshaller INSTANCE = new PointListSizedMarshaller(); + + private PointListSizedMarshaller() {} + + /** A point takes 16 bytes in serialized form: 8 bytes for both x and y value */ + private static final long ELEMENT_SIZE = 16; + + @Override + public long size(@NotNull List toWrite) { + return toWrite.size() * ELEMENT_SIZE; + } + + @Override + public void write(Bytes out, long size, @NotNull List toWrite) { + toWrite.forEach(point -> { + out.writeDouble(point.x); + out.writeDouble(point.y); + }); + } + + @NotNull + @Override + public List read(@NotNull Bytes in, long size, List using) { + if (size % ELEMENT_SIZE != 0) { + throw new IORuntimeException("Bytes size should be a multiple of " + ELEMENT_SIZE + + ", " + size + " read"); + } + long listSizeAsLong = size / ELEMENT_SIZE; + if (listSizeAsLong > Integer.MAX_VALUE) { + throw new IORuntimeException("List size couldn't be more than " + Integer.MAX_VALUE + + ", " + listSizeAsLong + " read"); + } + int listSize = (int) listSizeAsLong; + if (using == null) { + using = new ArrayList<>(listSize); + for (int i = 0; i < listSize; i++) { + using.add(null); + } + } else if (using.size() < listSize) { + while (using.size() < listSize) { + using.add(null); + } + } else if (using.size() > listSize) { + using.subList(listSize, using.size()).clear(); + } + for (int i = 0; i < listSize; i++) { + Point point = using.get(i); + if (point == null) + using.set(i, point = new Point()); + point.x = in.readDouble(); + point.y = in.readDouble(); + } + return using; + } + + @Override + public void writeMarshallable(@NotNull WireOut wireOut) { + // no fields to write + } + + @Override + public void readMarshallable(@NotNull WireIn wireIn) { + // no fields to read + } + + @Override + public PointListSizedMarshaller readResolve() { + return INSTANCE; + } +} +``` + +Usage example: + +```java +try (ChronicleMa p> objects = ChronicleMap + .of(String.class, (Class>) (Class) List.class) + .averageKey("range") + .valueMarshaller(PointListSizedMarshaller.INSTANCE) + .averageValue(asList(of(0, 0), of(1, 1))) + .entries(10) + .create()) { + objects.put("range", asList(of(0, 0), of(1, 1))); + objects.put("square", asList(of(0, 0), of(0, 100), of(100, 100), of(100, 0))); + + Assert.assertEquals(2, objects.get("range").size()); + Assert.assertEquals(4, objects.get("square").size()); +} +``` + +''' +<> diff --git a/docs/CM_Tutorial_Understanding.adoc b/docs/CM_Tutorial_Understanding.adoc new file mode 100755 index 000000000..4521678d0 --- /dev/null +++ b/docs/CM_Tutorial_Understanding.adoc @@ -0,0 +1,69 @@ += Understanding `StatefulCopyable` +Neil Clifford +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +== Problems + + 1. Sometimes on reading, writing, or in other methods, defined in serialization interfaces, it is needed to operate with intermediate objects; holding some writing/reading state. + + +In the simplest and most common case, those objects are of some kind of buffer between serialized or deserialized instances, and output or input `Bytes`. For example, see writer and reader implementations in <>. + + +To avoid generating a lot of garbage, those intermediate objects should be cached. + + 2. `Data` object, returned from `getData()` method of `DataAccess` serialization interface, should be cached in order to avoid generating garbage. + + 3. `SizedWriter` and `DataAccess` serialization interfaces, and the `Data` object, returned from + `DataAccess.getData()` method, since it is cached (see the previous point) have several methods: + - `size()` and `write()` in `SizedWriter` + - `getData()` and `uninit()` in `DataAccess` + - many methods in `Data`. + + + + + It is usually essential to save some computation results between calls to those methods from inside the Chronicle Map implementation, while working some key or value during some query to Chronicle Map. This is because, otherwise expensive computations are performed several times, and is very inefficient. + +These problems require to access some mutable, context-dependant fields, from within serializer +interface implementations. Only a *single instance* of the serializer implementation is configured for a Chronicle Map (either by `keyMarshallers()`, `valueMarshallers()`, `keyReaderAndDataAccess()`, or `valueReaderAndDataAccess()` methods in `ChronicleMapBuilder`). On the +other hand, `ChronicleMap` is a `ConcurrentMap`. That is, serializer implementations could be accessed from multiple threads concurrently. Moreover, a single `ChronicleMapBuilder` (with a single pair of serializer instances configured for keys and values) could be used to construct many independent ``ChronicleMap``s, which could also be accessed concurrently. + +An inefficient and fragile solution would be a single instance of serialization interface, static `ThreadLocal` fields in the serializer implementation class. + +- It is inefficient, because ``ThreadLocal``s are accessed each time a serializer interface method is called. That is much slower than accessing vanilla object fields. + +- It is fragile, because if you need `ThreadLocal` fields for preserving some state between calls to +multiple methods in serializer interfaces over a single query to a Chronicle Map (the 3rd point in +the problems list above), then you should consider that a single serializer instance could be used for both keys and values of the same Chronicle Map. Calls to some methods of serializer interface for serializing the key and the value over a Chronicle Map query are interspersed in unspecified order. + +In addition, you should consider that a single serializer instance could be used to access multiple independent Chronicle Map instances in the same thread. Calls to serializers within maps could be interspersed using <>. + +So, `ThreadLocal` fields should be isolated; not just per-accessing thread, but also per-serialized object domain, and per-accessing Chronicle Map instance. + +== Recommended solution +Use `StatefulCopyable`. + +A serializer implementation has ordinary instance +fields for caching anything, and preserving the state between calls to different methods. It implements the `StatefulCopyable` interface with a single method, `copy()`. This is like `Object.clone()` but copies only the configuration fields of the serializer instance; not the cache, or state fields. + +A call to the `copy()` method, at any point of a serializer instance lifetime, should return an instance of the same serializer implementation, in a state exactly equal to the state of the current instance, at the moment after construction and initial configuration, with an empty cache and state. + +As a consequence, the `copy()` method is transitive. `serializer.copy().copy().copy()` should return an object in exactly the same state as after a single `copy()` call. + +Chronicle Map implementation recognizes that configured `BytesWriter`, `BytesReader`, `SizedWriter`, `SizedReader`, or `DataAccess` instances implement `StatefulCopyable`, and populates it internally using `copy()` for each thread, Chronicle Map instances and serialized object domain (keys or values). + +See examples of implementing this interface in <>. + +It is permissable to return `this` from the `copy()` method, if the +serializer implementation doesn't have a state. + +Typically this is the case when the serializer is +configured with sub-serializers, which might be `StatefulCopyable`, or not. For example, the https://github.com/OpenHFT/Chronicle-Map/blob/master/src/main/java/net/openhft/chronicle/hash/serialization/ListMarshaller.java[`ListMarshaller`] +class. + + +''' +<> diff --git a/docs/CM_Updates.adoc b/docs/CM_Updates.adoc new file mode 100755 index 000000000..eda140f89 --- /dev/null +++ b/docs/CM_Updates.adoc @@ -0,0 +1,43 @@ += Updates included in Chronicle Map 3 +Neil Clifford +:toc: macro +:toclevels: 1 +:css-signature: demo +:toc-placement: macro +:icons: font + +toc::[] + +Changes between Chronicle Map 3 and the previous Chronicle Map version are detailed below: + +== Functional changes + + - Chronicle Map 3 has a https://github.com/OpenHFT/Chronicle-Map/blob/doco_refresh/spec[formal data store specification] that specifies the https://github.com/OpenHFT/Chronicle-Map/blob/doco_refresh/spec/1-design-goals.md#guarantees-1[guarantees] that the data store provides, and gives a way to verify those guarantees. + + - Added support for multi-key queries. + + - "Listeners" mechanism fully reworked; see <>. This has a number of important consequences, most notable is that it is now possible to define "eventual-consistency" replication strategy, different from "last write wins"; any state-based CRDT. + + - "Stateless clients" functionality (remote calls) is moved to https://github.com/OpenHFT/Chronicle-Engine[Chronicle Engine]. + + - Replication is performed using https://github.com/OpenHFT/Chronicle-Engine[Chronicle Engine]. + + - Earlier versions of Chronicle Map had hard creation-time limits on the number of entries storable in a Chronicle Map. If the size exceeded this limit, an exception was thrown. + + In Chronicle Map 3, this limitation is removed. The number of entries still has to be configured when the Chronicle Map is created, Exceeding this configured limit is possible, but discouraged. + + For more information, see <>. + + - Chronicle Map 3 supports <> that enables detection of data corruption, providing additional safety. + + - Chronicle Map 3 allows <> after failures and corruptions. + + - Minor improvements and fixes. + +== Non-functional changes + + - Chronicle Map 3 requires Java version 8, or later. + + - Chronicle Map 3 has <>. + + +''' +<<../ReadMe.adoc#,Back to ReadMe>> diff --git a/README.md b/docs/archived/README.md old mode 100644 new mode 100755 similarity index 99% rename from README.md rename to docs/archived/README.md index 394f0b899..6bb5b5aea --- a/README.md +++ b/docs/archived/README.md @@ -139,7 +139,6 @@ license, please contact sales@chronicle.software. 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 diff --git a/docs/compatibility.md b/docs/archived/compatibility.md similarity index 100% rename from docs/compatibility.md rename to docs/archived/compatibility.md diff --git a/docs/versioning.md b/docs/archived/versioning.md similarity index 100% rename from docs/versioning.md rename to docs/archived/versioning.md diff --git a/docs/images/CM_Overview.jpg b/docs/images/CM_Overview.jpg new file mode 100755 index 0000000000000000000000000000000000000000..a4d200015b23f53eb743cecf4c5ba7670b3c3598 GIT binary patch literal 87720 zcmeFa2UJwevM{=boDm6%M2V7f7LX($A~^^G!pr~z!wkZZ1r!w&m84`5$w@$R5)nxv zAUP)iLCK;7iSzd0XFBJ*=bm-nfByCEdTyNF)z#JA)z!6ocU5<9u|wD?$~6sdggpT0 z>H_=#0FVI0kfQ)T2tmL<0CEB#JcI$j4#M;Uc7mMxiv|y*5d**wU;}?{5b;Bp4um5^ z5CFknd9%TLA3(Nu4FGtLe7~!}&~|WkeMd9~?e2(nWmi*UKc#C6MWAqTK0sVlOj2G{ zPF`G!9R$FCk}_Z^0D%7l@{_+Oc;D;5i+Y0p9ftHAQUU-G1phBuqF>tr;rQ7W2>-8b z!GruLLmcl%*%TFjm8S*XL8$$8K5|9g^ijn0Ku*RREIGDb`3{|9R+Rz6eJ`h zq$Cuiq!cvdWaKpT)D#rd^v7vw>1khg9Pyv2VgaCN>-xH4j5CRB39swR9F%bzNK7|CBNP|ys@~9Z0ih(UH$Cc~iM0AlU zMXH=!^oI3z5|VB~-DaT2!7-WT%>#3rGMZ)%ULmnr6)l4wwrBu6Jdj(0L!O9< z2&E2LI4O3N0Oa5b2Q8ua^+-A#2la;CpCq{Krrd&})Qsq%?viWVL^uYBd8A(TfJ~$v zgK3yZW8l+2Sop6w;Qh%0b_AfnKdh4mPzE-PqqMpCxT3VrX>&yxMrm_BO#dAr=B~-j zEgTiL`8A{V1s`A3Ifc>bM@Q7}tDXzQcCDjoJxbS+gI!-tRWg=Z`~o7X>*t}!!#=poy-r@FbBAYX5D5C)X~$#FTrW7-Ua!(1xjAaRw}Wh;aBdTo99p6UrS$=6x=>S zDB_nFGU#mGKp>x0yt5rsvmRYBvT@>>zl^!RGfQp0v(CM$l#I6r?(;S7$=maM$~k)> zn)moCJdhSH9(pc`iAU8aoFG`Bj>9I&^b~pop3wtw6ed#Gb#Thxlz0EY|NO3{Rn|vp z$KG}HW>sBMky&RuG_<_etDaP@;^x*S?h6c!d=`v%;?gBMVzy%ei-txNkJ^zgFKQad zKR7DR>@U0!G4|M$D$MGI)^l`Bb#&>$1>2eL1+|F3dj+A2A*R&P8dlc)nERDCW6+)Y z>8ONahse<`AKqc5M9*9waR=r`XcM#dQ!|>I{*$qx9A7{11zx{XL}IsjCrz%qdRl3+ z!*KujCoB*)v@ncrnP;prUq$EZGRUW`hh(4d=ULjr0zJrmwhvFkb8X5C>SPr1hM|J3 zF5I<>Q}uBxkmahOiIgpa*z%giu}&kl9jWAu+?M>SOU~{erKDbEhV(l#&#frrls}JI zfmN)d*UJOoq25D-{gLqU(M2@$cyFoHL@)I1{)^5}FFKx@omkGW6kgQ7 zY{az$i`M3+jZLfUDC^8~vZlZ6S}l2hM4Nd1LL_mGQbO7WI(z*f%VfCnc=IEx`qKA| zcjL>-(T*6o$4O!S(h~dJ&akQ{*R%*nBw6>@&(||YHWO)I(X&EffmNdz{fBq1O@-SR zE;G#Uyih+vv4dK{uM6vkKd+hDqkE_+{1poj^0#2S4<8Gu64CJoB`$B%%V8CjVpt$j zFFe#S+iaogEkV?m6RpBK${Oa{qITEfUlTtK<#{_fzHP?%x>0qXePXigu7#$s@Yk08 zbpo0<&+@{I2A(^w70ZWTT%fFz;fORTvUI)*G}z_^&6U9E51@*hCpWC4#)K15R#uk$ zZF!U&A(i(p%@uXH$IYzE1VCfTcoO8p;N_O{x=-$iaTf;6Jn{PC64<4(Q{U&B|1s9C z|B+x>$7PhPW4QCs^gvxt{;2stwix^l(d!z;tCyZvrZM|)b1vjWoG+W+a6Ncr6>!6U znB^GR;smm6gL2g~JwnGz_Q1V1&){0ZyW{c;#YzPvA7wcYNCM1+d=IV}g{YD8GKdFy zDo2$Mk7EH>vRf5n<%g-r2TgHq@AE2u)8q2>dU4L4n<>Q06Zo2#)QR9%Y zV>KGsz1w4W38|-L(IQ=B6@f2+8hDA$%9Glg?8X9yrHQ@r?iS`_PB+dYii$FC6u+6* z_35lzx>ud>R*u$BoXIBV`HuD2c;aTs{?T4{-{%$1Yp;`i1igE=mDBn7gk)vCH<=t$ zqHAA~Cv=e#omS2bmGf%(%6BTvYVClO58Jb09OE^>{OFPO-B=XU{^^E`_MF!Ub~Xd< z($}yKmhBd^*;LrQ-O0M!ms^+(^I%%pH8trdedLx3wk+V6ed$*i&b--;g?KbY-#vm1yU%ZJ1|+$hlsB>wN_Kc}nW4QA=nI za)(Wqi8;}^U@P8zh#7c?qFpvzFY9_$O~OvoXK5bF#To`(Z7Nxhd1tZ6EJc-0o3$#7 z8oRlP87yzu73f>vjFXhUG$1*^gHz|<7ly`DqFwHLwYH-x_D(#P z*@5-~)qx7PlML6xS9#YPNw^q38R(EtdSHexk1jbsl3U`|pC3OvTI{bNQZV4cJJd6- zf>apa9gQfeE(3%qi%>rUe(nu|4@V+coxv0iz~uiwVJeNxcJ zo8Ura>jtclM7KoHfsTupN?_!&ukC@PC7)nHMKnrTqpc)sxZ+f4|4sGduLD0`i5nPV z9z1w9=f{Qx9``Hf%3d6rQRd7ZMs&1Ql~0Ckp>nh2%PlsiR{RThC6>x|PwYRAT@U=E zR90cpshUP`ROQYb_|83Vd%(GZ1)TJ;0L@ll8Tj69W}8-8-OR!}-8w6;&4=w>Z(kBA z7cbul%kFm;CQu=G)~=W}AF!)D<*&TJ+uYv5}|Yy2IQG)wqJGR`|V=^J$PW+ zIu@8=v*mjD%vlp4Q7d6$9YmF9n!n??)OFt5FVgT#BaJ%VX+zQzH*|m#_!jJx05g!p zbvi`uq50;=xuT}5q%&HPNAxZ~5ghakJL}KpTu>qYYQ)-}iT!!Q@#V?Nj@5&a7*iM7 zs;G;$3DX_-BRs~f>z;S^1dfJ=9bv3eVDo$0+sB~zSd1(;@)ja z&4d7&9oh!-$K=QCE@1(Ev0V$#U`Kra8qJlwH=BYtcx2lHBI;&!FGakI54Y=o({RkB zJZi*-ePx6ysSz<9L&Y7};ayzs{8%Mo{24Hk&lOS6ge6`zRno-y*;E*FmEdRVO;rLB`5|91c=kbw>L*f*_QAqx+2x2*r^ z4Lgiorl0$1bV&${07|5Azx!ym1t&2n;R{F8N!TaJLJ# zE^uQFb7K(ot+HCdm}i;!&T4D2?{1s0Q!I%fCm5U0d~L$fCCu718~I^1(3P^ zpax@y#i$}NsJ}R(u=7A5F$mOQkzhLMA+?(BMV)WbP{0*|Cj$Fq{qOLG?nulbUf&ye#_$I@nI{~ILA$BjVr+3OIAl`aLErtmR^VV9 zF?h$Z4V;JDe=Fu6sx=0k1AkYo5fu3iukQvua}ig<;dKP+hITb`goB+6YA6Eb@LhaV zxCBFxCsi~CgLXlpQ4W7)q4=JHW8oJ(<#)Ux!ol&^1oH0*Aa6hDanB0=@;4s7xd=dK z$Tyb(-r;k^bC^c;SEb-h`5N#|-zX1pICqKnAtmFFIO}1lUk>A>hanh14s(I76x>Dj z*Fyp->>=&OAyx|zv9||PkYETpKfnv}7bYb>;RiUe{fO|ZC z=pY#X2OS1ff9U!*HVl8L@?o=x+>r<<+}#v;9``UpeyfM@FbzZ>1%Jec5pDHf>LPY< zLwmUXh9W|{Asi4t##s#`T z$#?W`#H5aBH(wPb!r{B5sJ}H{>pL23IWY`wZ|iY51e1Eg-7tScZu%YlJ2|ljCd1P8-^!cy2B4xaynCkMOO@gK<# zpV(8lioka@sLslt4&iT4F3w5(O@)aM>2MSPMK2iO9*!sA?+OUM1At!e2fAR7gD%xS zF4sRU*FP@TKQ7llF4sRU*FP@TKQ7llF4sRU*FP@TKQ7n*Z@XN-**hmeiv|F_l%P?A zGjON@70`u%2JApr0z2r)bOd1x_;WvmTtSMX{|yB@pa%Zo%%wy7>6_1j7~H~hSOd7V zXW0>hag`SlLAeXt;x^j|L(wiG-nOnHV#1;#z-eV~S6iqv9K&u02Q79*j@6nv4t4}g zk;6<%S5((k4ep50@^yn7`Cc%F`Z`19U>wRy?5Dluyg;22wWZxD`&V6HOp)WSMtOO8342Khqum@t#N_1UL`20! z#Kna`3L$qN6vozD2<6WCy##f*JJbzv=(AqJ%E93-+zx74MW)b{5zz&y7Jm6cZ@9x z3fERw~~);bQ9m7jbfhJ8-c7=py+;2aPTo28vnYSG=~G8n|3xk3fP6?x3rm zU0Xv6@k_4(`BF@j(D3qm5;4;5107K&TDP~?E(6aWsk z>>C>KHSmx}R?%pGly@v?P;t2ltY@ZTz)$bZGWyRGNHX8qq&ivJ+% ze~A_9Xp3@ygM+FF2d*(9xZ&qpr-=Mxe;ua%GE{vh2gg4g_+#Kz`aVGZx&M*C9|`=C zz#j?xk-#4b{QpP-KTS6{3Y=Pafrc4&n!+5MA=>C0>Ydd-uW@K&QfQq=pwM{Wy0Qzn zaRfJIG{0!UPW%>JS0)9QxWN@a+1Yx8X#ubOhEeF!sIpbhF+*&rB8^|CCAIHHC z-(bN*ns4wK91L?ofh)&&IG$Z$E-)P22*S5LJ#Z_^_~$`5*b@Qw0^vRo=0bY7AV3(m zg@MimZVRq*6X2E_xiE03BM6IvFom13A!tbxoCXiTP&oVo+x-G#z@=S~7EnXG`W!BH zvY&$Tv4gg!EV~xm3kkY93jmIf{KRoR0o@fh0ie9| zm-aB=^yL>@907+4;{^KS`fmw-Q2y7zFZBuI>ic2uu&aMt8)wI9H56Ry_Hbi&2i+NP zcAIvZi&NGBbDzuXQG(2@iAmmY&DkRSbap41fdD*=ER+lOy@55i#jVfb5s z7Y;`8+!2TSEdVt`V|J*Ao98zY0^E%d-26ff&;g79E5Hey0yn;h12TXDa0bu-&H;LW zF>n#E2B4tl$_3nz=LK8^t^>iqT_6I81`>c|ARWj73V>pu0(cJ81I<7i&;|4Zqrf|0 z7FYy60h_=scnXOaLJ6UTFhbZM+zn9sUyjHUSXL!{Y+8`z-W+WCMRwULVwk5_82NTB=KP9ds?jfEf{z^hl!bT!aa+bt` z1Vs`=5<`+p@`9v?=IvM=Q1r%3bi%0FLexc z33Vs+(h<@lJV(@y*d7Tul6a)*$l#F;8d@4L8a)~pntL?4G_5orj}jl{IjV6KcJ$`a zw4)72XK3+hIce2sp|m$>(`lP%XX%LOcG+xB@Z+J!3y=37-(h5D)L?XG zjAX1}96v#Dg8ziU39l26Pc)raVxnVGV1hD*G8Hq8Fyk@vGaEAdF{d%NGjFi4vS_ln zvc$7Auq?4Mu%2OcVtvT^f_0vamQ9Hb!S;}?j%|URo?V3<$sWhv$o}ah^GU6f9w$>y zzCF3i!OLO75yVl*G0sWGDa&ck`H=G!=O->UE?usxTzOn0+@##H+>YF_+%4Q&JiI(+ zJRv+4JRf`aLHVjc_cF^ODC%@dr$VY9HE@5T%g=@xovrAd5nCq{HlVGg0n)d!pGCRr{Sm5 zPtPcFDB3EfD85r-SGuh9MCqOKNo8B*RORV2oM&KXGS7Ta;Zt!^d8)FaDyr(HTBf?C zCa>nN_EH^RT~j?&{jJ7P4HJz7jft}yXC2Pwo&BUKrRk^nQj1VaM=MfmP@6>?s-2_# z>72|t|8tEx6goya2|Cl~`OmwZuhzxWJ*WFXckBZ91=NKKJwQ)e?}6U9KCk{2{Tc%z z1AT);gE>Pn!>fiZMzlsYM!80t#;V5Q#-k>uOgv0pnNpixGR-mFGE+B;GJ9t(V(xF= zcJahT#KlSrVhdA?Op6Um4a;cD*-KKFf-enN@mhIXwOXIBMq1a}P}|trlw2mfY<4;K z@}8}pZK~~townU0yH8LxXe@LQb_VtkHV;>XN5MbXE80ide{@iEc;GPasO%WyxP(wc zBp}wDw49Qiww(2xvz)O=Gi0F)iOXe|D%4SwGpZ5IiuOi#y9&4lyS}?3es}UGq*Hv z6Cb*5b{q~N;o%lNkp|H?5cSY~U+}*nez4zw6*!{Ts*f9IBx8c&^ zk0Xd8P!R)>%8}VoG*P}$GY@nhls{yD82WHM+9tX+MlvQjmMqpI_FdfhxXO60_=xzu z1jmH_MAgK?N6e4zJlaZvCG|W$^SB_HCHZdh&J)Cwp%l%O@>HJGm^7j^&$PL8)AW`M z*^Hdb6Pb52_p?x0@3IZE8*^lGa&nn-!}IX+Jo4tBUV7SG z*+S9fqJd(a;#VcIB?YD2rH{&J%RACtVXyd>-ov& ziM8~#;V;Nu+>H2c1-l2ZxmCdWs2J?o#MuWz;O*&1lo6j~kw5YVyy;gkv zyj8xn>W%E1iZns=H5jzvxl!sqa=;JVQcx}XUWOwx17~xpx_>u9L3Fe8^NxsR#chc`_r_`s~r;VmZ zXP`5S@7>?;&EA}&oQwIu_95q^_{Zn-XXm>XEEi@MT^Dzkf|qHQAFZ5PDP29Y+WyJ> z)9jkt8us(OFN|NZ)+N_pZRl@IY`SdjZiRkj{F=QjyWO&5w)0`vYmay@Zl7iv1H`3yK0_;#-O+o|DyE5!SG;s3aP$%#9~i}xep-vzL^bIp(*yeNDKco6vK zxn?8qTr)VW1&Hv#v$;PmViF=k0#bZ3@O(FTt{EW02kZZl2qxhZo}0W$I<03HM% zJlTwo;|Pz4)B^&Zd8Q#eN_3K#mIELWqf-&*q$f2{Ww7OvIA(a|`fY4rwyMcLd6P{Mm&^yB?YyZWAUrt_2 z`y4n~b8tjBd3t&K_}&T*xqT<}VRTGvTzo=$MrKxaPHt&gc|~Pabz@U=%j?!RefRFEWiAp1B}eJ!<^M~cx{S_8&Q=6>VBSqzR}6S#+| z%*|Ve41eJ}@i4s?u?&y6xF1zye(8sTG+7}T%cj9xZ zT8HPi`Q^`9IQfPqb=0 ztzy{jKQjK0%ed!{jQ`^@?)@X<|G13%{>b=0F5~_`GX9Utc;JtWf65qBA3aw2T>Y-D zM4OQn_dV~0_c_J~`W` ztz2sB+TIuece~FeQd}-lEWY@KED<+*283>Pq`8^uK-}o>;71YH85LrlY!&=&LD*mi ze^XCZ;wPPfUTmft);9T{{2$awPc*lB9&FytuYM!c8FiPMXnpC#BVilYuBAuZ9=4%D zX_#5puP27Hv(M)lS#I_Sl5!R#`z^$8dP-~dOHa=r=HBYg}9NBqobn7gcQ_LXEcA zdxl%Dc#F9~vu>>^PcZYvrzCInWMQTT%C0QV%MFw!_S`7&BqWB}pV~TRXMoJl)2{O( zk`Iez3xA6~homP^$P!ai(V(L(%a?5`v_>o1fl=>|$u-%&oUAT!-*juIMWJt*8Z_-x6yrb+sE zVd=%dgo4-8U3XHm(juQnY)I3YFZn$Ec@6en13WS1_tK-+^(>0@2wa~=@#YMcQH2!5 zY!9!Ayf^dm3TzR6uyT*s&WpE&;^9}+!pLz!)A_Zft7SYIt{Y!R?zdT9PanCMFlG-c zW4Yu#veTULG}N4jRM0ThHwk&fS9)S&VCQcI{9rT|bndd&*{7b5_E$IGL&ESVt+e?I zUVbv=CaiiF0omCdS{!$;zs&cnD~5N)&4jHg;&q!>c5c$XVZhAzz>&#}z@fy%Js?|Frk!@5)_I@94dT1&XYQ3qnpFOU;b(*f`t9G2HIkl7ohBFi3M=?k)5PLC!jf z8;dttr-;YBi5J_Z$E(k}ab5l{Bo#CF4&pa@*Kdu*FN3Z27ghS*7|mxT&ey^MQ}7M@ zkZqfF_$AslortXM`M!_WVsd#0ioMMr#&P~8ruYEUqu#~88YhLmf5oHb!o_L zJ}|fGoOzFKE4;a1vRA2qpDm9C=0x6J>a>1b`E@3E)oSg|aE;7tpw$W^pXXaFFvL<< zB||hbLKlx}9!hezifm0QhaNL~egz}m?3C87HrMKxkAKN$BY$)LKr6Yu!fEf-0y;!t zb%x0OX^qT#qP2q2ajV!?( zEhR(A$txQkrn`}QVq;TeSeCz|utYj+`1LGT$9w6Op_iBQN{slli#Ou#neZs(@$oab zMZtKp5avgduKw<1^(~$g{T(<`-aL_@;OZe<@zF~G7G{^*++%>aKM5KWSyWf zkIk|oZ`_JxJbqV{V`gF}WoDo>@O9w<9rrWkcn?8k<^VOL<$}!Zx^l5HLt#!vPUt3E zpxv_wZ5~UvjYm=&w4I^jmDLtr*=;TrF$jhv>*`x`8RxRE>}Ht|)gt)zK3C6p?2#VW zDuQ2bv(gqR$^s1}*tcot&Sb$`F=_=3HudAQu{9A535SUd2V6?qIRJwc5?RRxT~BD8+GBO^&W08tc+ZgS?4wGqsldF2%vTjlcD8ak@iWtIIJ$ z?Y1TE^LvaZQ_kKA|C&Y~?#E1RJr4&T<8;*SXE=OF=(OdW>fBR$6NZGO_}dT_cHgKoKos#A-d(vQu<-(; z;aEUr8w)IMRUSK_sDstbDuAsn*2Dr&s-u_8ep`Et0j+^=JZG=~3|EfF>Q@%UH(21IrSkUMy14}_>UQDC{HK}e$Y#5n#vRPc zURF2qLc{Pm362`KF~+Cf(%z7cqH{w+l={v0Kh;LoJ})nInf?snytT$%9tpKbFd1^< zPwU7&!zc+nj;r3S^^0{uOT8S;pY2MY*M3-IB&m-u`Y2%Z(jiTi*Tm?H}@w3?8RXyiw%HU!ja%W_giV&VQ1dh?S+IR4n@Q95lP7>26kLGON8i%V{^n zC#71nUfd#D>CSfau#IS79CdTu4VL^q%UGAI83$&Gqs0nabG&FXsH?ef=49!H4b$%3 z=~Xj_YMz7;iRdvCbMT{nl65>qqccmN>Ed30@?PM}zG3qEEy zW+M6i$>$1pYrMI7yru7OnWlV}O!>Hlnnl@lW=D7PEFue-d|>CoI1M?;!o@7s zdk$aNtGl zKV(^*Tr68QJD_`Og9WPYC-0*cC;Nnbu|TGXC-}rDgzpUYtR!SUE`EHXT6-!tBPUdD zegTo8J-@&nt=1$dq29qYr- zl;rhC5lIQlHm+kd;ur3jL|->G6jdR*dBj!a^`cBgHTwVZ#Lag6V#sI>^vVj!ltpa) zWp~D#S)QjJypUisuy|H-H~QlF6WYwz{3E`Yejcl7V{U0DC5c=@3Jc@Wo@qY*_)PjX zQSZ5Jvox!_D62`o;mw$c)Nv~fQroChZF+vgS1ZoN8Y$d21+&l&UEU;d<=<*%bYP`bAV5*PV9h$TYE&rfdz33p;;zjqHBZo0o(SnIy{TMs9c z#Gkv$$|RDQcM{?H_03*}4sE)Sl2VjK)g9$)oHxI$`s=SPPBX5prX0ZS^IUB>1LT(i zT2u0dEUSfDN)?=uZxhY!w#s|+)7ykVq51GHor6p422?$X(kfatI8F7DX|Jz1h~ir-8}G8s5I0*d5K>l4-J;Q@J+bSK@j#VUmgVD*6pYCbZnk+S0vE#MKBX z{JIUbeE;Bfje@W3XI-n{CrvYhq;bt~F$}GNAgN^aRDU-0=s=sz>)siM3wx*o(`2&j zokW=3W|A4|s>l4}-KC`@N$c}3zAU3A7slgjWXeCjh0TeZyywl%%MOiss1P?_REjC> zCJZL&$M{xbfd_dmTLt-lyP$-*-yYa91wO~Kj|0I^a2}gz!5^%?=r?HnG4ARIED*0# z5xB-N9k`V+KJlq#wF3)Wh>Z~`mopf8t^AI)l9OSD`PO2ma)#QJWY zvxFoWu$9nCK^P_?;vLMGF&j|gtyhm%VD|) zFO}NLI||(<3T;O{?~)c&kV+v!xe#0~{8pu@K`rM5#Gbqgx&WDnFK@(*x^T};c`-BG z(beN5P{|3(zjzz(scPt>C}P^dSjH$MvMTz?Q>407*9>=SDB?bW&xttR+@M)s3n zPEH6e^ET&GH%A&KW1YtmU1EJKNyEAxJQb1-{&e&0(?y!71oeBjmyTA4=14wSY~JiZ z8=+(j?$@V^lWb7hOTkTDqx+vo@W*68*iEzYgG8~wnqyPoNO=erxRScHP7U^0E3Y&7 ztw_hZnY)9$GSpj2y9dt7d*165sLlsgoW<@Jm&+N*>BC*;`O ze8Zi<73i^2?DDp8c(le1y_``Y!PX=(ZX4gYJiejJpv*@vO|FnQ>YYCs-Y9LF8OnS? zjl*-oGqk`lhc{^+ns@sGCxTwDdh232&)$fCd^tiU>@_$`Vj7L^A0BfN)?9KkpA2!*-OV`8ppj&uGhLL| z$}bE3m(ij%hTaSCi#>SaiUl^(cPpkqLol{_T&^m zL)adZb+UwpplX4%j*o|)Qh~sX+2_Kq8f(hWV$02r8$%&+D`*G)~l&o8N4rt8HMT!Yn6WE%%r#G~U)zt)R9`?K3rcIzT2+V)8Z? z5X>%m_%P9HcA|T%u5GlYIFGEEeZoK`dKDd7lbip9oYPn@X*e<2QTuetUNMRp#{E$3 z2-D)YrPq(C5aISg)=Lz!%-T#UdYsBdMPovF=K`xq+96d2foy=SqG2m_P1g&dp-;+bJqDcZAQdMzdXC4Po6Uuc`A< z1Sz9PDztA%n{x=p7vZPSoOlalNOt zzkECy!}tWQJsrc~JsodqJ|XSsa33CQLIn})pba+N5s%O$9#a#fJ@e+ltJh5?*DkB@ zzZ8E~c;t*Uj`Z^DQI?>KF<}uJnhRv0Msotb)g;cpAiLMzjdOze8SRSEO0(F2L-h z&BY-?rgY6!c54U=RKtqfmPEK2VS*-wbjAarA}9dw#hJ_0C$EQ9jjxU&*s8C#rU1F4 z56&AWIywdPepu`d--_H`Oal?Ja`=C|HlPpj5D46H5W6C)vPco$*ME}LXy$?&<0!}uJ<(rB;ikT7Jy#x9m z1e~U``mSmlnK1-}^51!7X(TAdr>w!cjR@SD+mhEAjUAOkL+_%6uS!M<#B&)JI{Gjy zNO@*4n0L|EYhG+YY}vklSbhubb^oo-vouT2SGO(ObK{tE2d@b?`aI~>4eUU5ztJC< z5odBQSjZNajFyxt?XlgW4oi(HIF^+Yq^po)sowSa4N|3O(u~4AVHZfVfJ@s7>@(Gh?&Pb49DDy1ce@LIzVEtbwr=q&HJ#G!G12Bfyg< zfi9n?eSOEKm&HhtR@5RpJ4Ys1x>$>}G*8Ed^;1&z2R{F!1pbt7AFOZDXsl#5e&(>* z6?+p7(`A8%y9TS)-R4Sjg)$jU`3t3g(4OkMws*IhzrXh#-BpmVc zz4JBB^2@FZtOSN1S1gNQD#(+F#R8ffh3MIZQ^CU%yxclvUmigDxw8&7HOrl({RU<} zx^bV+%u_q2MaPCV=$J1P4dPnpVs$o8(XKgB_UmWAL3e8I$b2El6uqLEh zToUZ<^*M5_SuW-b*M)#5?yD8`+VqR@BO&^!s{vIGH2tt**+J#dC1G8cgUYO|Z1%wL z-idV?>ppqQDX+JkV;3T(DtLIRQ}w=J?wB!q6kax;Pkq#6I)I<7$+}=1##w0WyPmgI zP|;0v;D`mdHp3gkXIDQj)>Z_YCLW_2_&C0MZ#U}4~N4Nbu z2NaQ28y(PKL++zc&*e+2+AOALFF1?*SV^E6G;GJOo9Sgvtos(P%^R#*S^j3Nd|z3ZXNtoD z;SCPpGRkQzaPUr~HjKCsQw*-2l(q;Vm_%1bhT;uZ7Jf#1f$_p!59cLV6Hq~5Q z&T$tE4f~eBvUYt{BfEP96R6I`H(t})pkB5<JuHSN)vdDDp!t4B97c1Q*#aCfcnaE^=lEj z_c%N+u90C3f3rd@wd`u%oo!_YAQBV+R__8v-3p(-3V$Ih}H2puonHSNLzE0Wf` zG`~;rPjCI0;%`{{#ZHSLW|T}ekF^~XcWi7gF^5?~r#+j8CZdNkM|P7z4^3r_CSnsc zFjf}fA~>g3AOqW0ETRuMCU-rwT9a;3H!?Ui21>!>(L_xZxvpB0j8{{Unzl4C+wS1z zf1_@#>qGIr{J2TQ$t9tUk=iYhE4DSztO_%y=}ohi3ngRb=RGHLMg!}MFDec`-?}Bl zBOnoR-Oc+s@{6JI%9AdSm8ZgNH+=@ajz`SL@0R#bgh8J=8I~LoQ$*~)yz$;6z(qkk ztL{nug;FeVuCR)>M&z+p#+ZbD&PffP4Atra=L@yn=F4@n%bzs^k&jE$Gv8w9ZS40q zmzJ&jmn9t%H6M=lULT0QJ5IDV?{I8FB?2B@I3Hq`=T=|JwMXZ@Z9gL%Wk;(*Z!o1Hvt)o zJJ-Xb5t#+A`lR5e^8^~-@@LiM3VE zz(p&KeXk+^lYM@sQ6={Nu_mb3VoYi9t+PFyD^7xnyd(OORp_d_oZ2Rm^@@2P2CES- zugc#*ZhmO8emG)zE|0LtGyH7V?(sNyT_MjsCDZHJTiw|%Wwr`3{xZ5GzP`~1&lJ*7JuYve z?n;@yWx6q|WO&J_E^i5bhbeAJ(AUxDarp)C{nc-W1!Uvwngc}w7M4#f6FJWWJ7gUg zFJDdAaVlcEF{6y}zyg@w0fUa6l$g}J_Qr-=dmiuuC)TTImIo^9l5(2Aei z5Hph0F)%oq=EFDbdd5oK?bU9Ak7jgj`)j>Ij$Tp}?jXLW#io8_+!p)|*$Zfwbrh=4wp~}2HXxzHlSlA6OCg=X~$jq~b5@#&L z69;o0x9sOzS4OH)4R_u8^&QIROg8-)=5m|gIg53X=~h`sqB~Nk$Z7!EW$Qb9!}{us z7y$x8rY!kg6GX?O-l{JyW0x9OX9dkn-IHD@3BP7le+s_v zwR*^{aG@{#%?(AvNQTu?#VBpUM%Crz810_3gRVD+t>Sxpzoy!~D^I0L&A97fx;x^w zQG0ehv$b1u=*aTvaGkDqOAogR>icF-uUjU_(?3o%N6eQqf3dszSkw3+{~{C(AH2Hp zCTbUb7YVobrbF5qC7rnn-A5eY>LVjMl)gg(x2%wep4W0V0T-@>#PSqJ zd``4!d477>vAvBUwp=7b#r}m!b#MC7G8axoR=wLxbsw1=crpff$J$M2^i9VvK6`4m zKo>2oJhdtpFweF=2R|Bvo>{@XP+ zoWZ1975=pp9=L321->qNc7-RD-#@4h?3AoqYo52CJoo?_WF0VYK0=jKn7<>8svpjI z{(#RwEvyA~zaeb=sODJ#>l=`Y%>iE5R|G+iQb;G56TzIOML-|OPGr|Gw2@94|;@T4TE{J@2ZSX>>iOK7aS+cvyY<%vt zZi^SWDjt~Eb3YS#qJ4Vn=t~AoPIdjvTMp4-0%yqS31!s!XACfss$B0FgdNI5EH2jfbWfJmyY)>84RpRhwod~t* zS#&09bs1V)R&)1)EiQ7_ub#HS)0K0aSTJ`2B$1>S#BE1ye!JfD9)`gTRx z9kaKOdz)G<0~Kvnm`Wr)75W&Y=?rxGVi;;9iVE%W6D@T{vz;n6@=xh7S9C_Xb59Cd zjF_MDORBa^tbSl=AEpA_hF0Bw8LyCydDU|N%<7BWTums9uStD6dAD$2<|WUA2I#=9 z#_KhwZo6ji|7t0$^V{%nQ*0R%YHIk(R=pW8uBo>dxkOFY8|HmwabiG)^!={(d4cJD z)Kmf)=tWfhcx9&H$rc8!+xdKwYBy0x+T_!53!&OO;w^f|My4I5_DhMEr$ahVi68x< zU4YPA^n6z8-@!cJzBjk4@I`uQOV<|8l71pc_%=tuttFGbVeWK)L%FZx*{1}D<Z$BvvMI;@vLQ*yHE-9m1aste!n)o*8`NrkEa`QS+Fj``IW? z>NQP@EKotU3s;mn8Tl_Y`Z-e)T?n-`(6!W6Arx#C4`MKN!rVSLA3z>l^xloQwy!6; z#p22OyveE+I^Q}ldG2M(>SSbcTk`ZEyz|lPjlrv%0X@nXF+KK8x?eWRd&6WKTzceUn z&qUY{v8q^;0LF+fJw7M+c6`iysIosM-aD{o>AsGvd5pET|+*paR-WaUrNtQdAV4eEv^c8ImBTph$ zr(g|xb6X_QeAY!c`;o}_59(DjM%OnuCr<2tol3BA+|&%?frZBj4ZkM1W#sI8J+aFP zZt=+JvTWplhW{|veK{=PZsy9k8Lt}Toysy7Bw%H~3EbFZt>V1; zW~(CX9vS1M8w=zoShIF*daS=&Pyc$ORys=NKv@wqjPzv#cY6FsKZVuyw2k}zJZppf zxevL^eRN!RaDQ!pX)$xx43N>Y+%z3&U8Y>DVB(Bvq!^fJKhrnN?9=)#uP8o&#(ZA)m7B_M#6i)e$P4+|<0dVi6c`H5EUkxIa0n;LF>@ zn)7wHDs3bJiJHApB*TJ{6M3M}br2%8S8R_Md(EDua zj4VMM6^orH$5`HMxaYMx2r(lsuFaj;CC%)3S;1}9waQ;7R_^|jNlu5}>eWSBnc0tM zpMC^clo$A&IlpXzsIMm2Z}7~s=_d{Ehf~CSid!)nzb^6)rMwBeiLWDrfl2eYuFWNnYz0sRMOH~ za4|l;ETiKda3mRdeOky12Lc&0VyMpQ?!M;K#HZZA8ar~Mplrhz&z_w3V<0aGXm{RQ z9Z5s1FzUXU&pcd*u0I-hq#>uLC#+{C*rLr+n3*qpN14U>7XLUweB4LsDYs`OZaqar zI`lV|c7GL+?Zn8)zjS-Cw7GZ9IIJ|!ByhE)Z?yMl{Iu`~*JoWFJslPD9w807*?1PN ztGRpd6j-J-P`v3A3_yhOXTofq!ezn5oQ?g?EqAoeiklU^%CzH<^wJUh0_A|P%qi7 z9mVrd82TL6Om=vTsw}$1i#JL?kLJVDp_UarTzGyT1U`hI3TlDVoboV06DX@+J8tS& zx3R6>SeDhhaRNH(?5Hag00YL>l`aTcI=CA~`6R*|J4eio9#`PQFA<4H(MGTDg^$ABxUt`u`*7cZ_K~G8FaaE8M z#pHWR?x?2`S7;<&PzoL5yNfu>^rSg`ny1_H_n6*)j8D`(`}zEi9;eIk^T7|B_vd-k z+}g`u_9k65pu8s$+;$-4P31dN@()RmazCO+LmfdW;X6=JDO2*>JM1;XQ1}HN<2v#i z1ett^*DP!v;s852U_57so}@L;v;r3nN;Ggw8t0NrM-jhXSg!j2_XRPuZsx~?7!mW4 zh-dnYYr=$t*!88VV`SWs-qFv}5LfSZKpm@$goMKu8W`3G;y zB>M5CGa`{fH6~4^I7cs${#_5-qIJ=R$JpJj=ehgcsBfN}*#Xo*L}`0$M+v!l)X~_9 z+14XlG)0sXwoz3fDnEB#w*x8Y1=KnmWcv-38STIIGIsO0*4V{`n+>2-ne7%I_nq6r z*2Bk#8d{sUeWm_Z{uPp6nS&NNS^N}hV$~HAKn!iloOMt3&|znXCM{o|CTV95w$T2# z`|)1>D-N4GN;L6~q~uNvn{Ec&&MBlb6{ak=5%2+!OC4e7d14?tjrdf~7lC$)Zi6~0 z|6u)$9fsY$6e={d!5riwROVI76BnX1u~HnPLKLEBBK7_wNkIN%0>^2oNNaG&QU92H zz1y2GQX`-27{qfCM*;b24*QB@K>78lpZmA6mXi@NMG*=qm`XYDc zmhrAyAYzNh672SxGK96Gv5(lJg(LhW>+Bn=+u2c$I>z&^w%qg8;e#o{)!n;Sy9p*o zJJHX_I17~y?MTPp7D6WPhJ-+t_V%2In)%_11S!c$KRnC?%HO2d=J5|n*>Fl|>BJ=V z0(00Xj%LTfI%dF*W7KFR7U_hhORAr+*`yDJCO<5na}MLTQ{(P8g`~skKL06m(YNVI-Ue@cn<*~SKBo3>?r9igRxiUQ z@5C^C>8wet*Tt~2U#ByMqK14vfzKSB;Ue4I zC1s$assaJGHIpHZNo{wNM^Wrqc>1~72VFiaJ0_rKAuT?+M*|9qT9opZuHxLHv-+o> z#R9%+y=31>PJ6=L#u^^9hC@{Q_DSc!J?Xg4&}utKb}Iw^A>q~!4`WRQ-*}YwYU1vf zz7KPtcfg={Mrsp0Mao|wYEVp+cNFilt+P0Y%zMKB@a_&kBFMO?QI|)ys%f~ZPuKMm>eM8#Zk-c=QQsmjz0P(|H zB1y!-0S^8Y?uIm30&Us-`ng|X?s{L;zr_1+2Vgdh z)D9~-1Yo2Rl7)T>Cwy8+ir$qSmgVN<_-*V=$R20%IgOyU?e@mp)gp%yZntqo0&x;< z*ZLAG4N`i&7QRG%VK|`U(wBmPLEs43Z5c@q&tcw^-LsWk#GvO3TM_k+?b5K)e1Far zcyrgmwem`ulF(W-)|4XPO-;9en`H1S29*%@+gbL~>eZv6`?XtDrX8~>AJV+9 zoP$_LMEo*@o^8&{Ijey~wAG~L39-(Jx$22W`@mL>hQnU5jZrBPcaiphT727#2C+CN zkw#sPJs>8#A)A1OEu17C8fNCITD)c)JoFv63;|$w<-!)03$elPSz*3>4|n=!`Zr#0 zMx%zXKh7Z5hdX0HMf%6CWM;QNsYpXj<>h0u)84TvszzlNXwa+CL!^Y=<8Qjmv*o3- z1nN#+sl3W7ozqfYzJOr+x=_4US01Oe9#5sT@H&h6YB49*lN;L@$D$OD^@hs27`ixN zgJ+!mzVaOSj9-tUfn8P_=$vKHmVo?-&HoZ_hwK&Oarw#2CEiVB51><`0(44QfKanc z95!KGe~CAz3lQ*?Ww0!UnoGQNXgMItB>x6nB5Z|~MAo#PYIw+E?pFgal_~Umci8-B z8VNhmlf^pis4<#5-SgF@tfFJK)11~(s?^aNLlpA}>q+H^TU3Uo*_!izXNRDkab1 zdPC;Dd)W!!57~dL_x%N#Ws&WA<1-5uA*vcN((=9 z-oLQiwMsayrz^Ho?+TSFwf0Sez`|=bVb84_gvOyd80@t&QF znyXXW4Rr!pTs0E3$|wsMn^|{o5}F(vvFQDlKbiYu1@GIe@yqCrlG|nh3pUuHJ?5x+ z=ouqxRL?qB(Mc)9xPuCjlacF{)iWgJ-zlDQU$rLJ<37E*R&KebYm>y3{kzeQn#w4U z({*N9HQK;?CB&2g*IzR~KHx*?RE1DT%JEx0Y~KXGIznr+EA4bhyDaE|)t)kk#d{|T z#E=99-Rc9M3b$Y475kpP-d!Zbq5f4_?4R}~J021x`9%*>m2c&D{%B_+Vu()Bl-F2``bW%_4(+-(h{A#9yN} zlk`PaGRFfzm&pyL2m;&wP7OO-+US6t1}NMVSOiNfAe@T%Oz-_Hi?XUfF%Pl|@EM;7 zO58ub#JlG5NPlu5VoxfEgg92|s0t$Km)zoQM;jbL%SFxel+#F9WM)kjzKyzq!mTz^ z%+v}Zmex$=EQVE{odk0+#eNXHxla1HyX2O+dPU(3O0_}JnaIpF?9L*=l~h|ReHp6< z!ACRU!gsxo$+1yko);H8%UFW@mw0>c<~2^Q0!q^hjtSV@)k{3Mxr*#cej}hi<&?#6 zg3rgMz0LjK=;r(HN;0!)sZ?a<2c(0v$2GVg_`g{zh~;|`r8Mx(lEPm@LSte+)V$8D zKk+wdR6Vtz(e=Ai+~XqFrBCYXqBxk}QMib0OG!Aunq@Xd`)d|bud3^A+tk@=`UGp; zp}ICII5X+)n{T(hKdJlX{OP{7ruM|xcu#(9L|Ot%0L5fyrCm5){RU>lm`>8Sp??Hi zr6%(AX>>-3T~Ma3?#3t{r*{{$D5;DW#EYcUJ!q6 zE4P&GKF=Lnrk%eCQPm-lxgHpj&i6p;x(0Kcogl5D-Iu9CcDuabf^VEHQ6!~3OLP9$ z%C5+dzB-s$!Ht|b*EJY;TR|f?2o=DkA*FlQ=Z(Lw*PMKFa-K7;l{9c3Lzs2WC7IMf zZBT5v?MNqmk;IE4n!3Fy0tF$r(HWiR4cO7UkP1%6(ZjK3vPsz8^AQkm-mv1Hlc?G8 z)@(%9DTt7?`cAU~#0O=VIP$&X?B?zx<8%m!IBZ;R<6w?6q&PvP!jO|QFTvJUrp#z! z)bt>6t%hsESJVLc91yM@6TWd5!M>}I5N-5m;iLYvKu0YuO)<*@udNXC`D#QzH$$IP zx$EEpk0p?bmXFSgXHmXioRd|UckF$Vp04A^=407?VKNJcU&EdZd)S;JB3AeOFo@ya z1)7Ctq>bJlp0?H&#e*~Bhznx=H4)@;`L;>}e1M zE9iSFsu`dpe$=4VP|1L0Pg2ni?DsYHwdQoqN~G_r^Fy}y1+C7wc0!7(xOTz_>)_zW^!+2GqB+Gc` z7m=c1Ny)*`2Y1;LSoJ1v=-r|bq9|ZVdux8QydZmcSlfeppQM2+Ku5xM7g1z53dA99 zfDN#roF}wS1F++{Ma>9?EfnX!yS&|62mAgV9ErUtn2yx}Wck02N1x9_0SfK8{v1#> z?ZZZ;jR?EwQT3wBx}}N`tK5N&{lrtvRrjEbK1aHh9dAwnd=^v#_tT;iUw<~zNo zRp_%3tQU#8$=cg@`%S8kLe*@3^dfe6W>cP| z+fIVKPs@*{&S>ExpClYKnn_rNrPxl@%~a~2<4L9p6b5SEqTB9V_QoAcdRCcCf6fB; z9e@&dK{5T^8^$bttSU^n4{aXf*95*?$;W#!9XnIA)U^cl|60qhHppRZH(v4V3;83N zut$tjLF`lh2@l^#5pa^hDuK$t4*;;uTQBh*`F1S-^Z`6mz|-51hdpeFYFzi~9`5scs^UTsS_ObyM;T-=*_)%FprFWSepvHm{D@4TXC-2JPpp1#h8 zVW<)Jy;OP8=(!CWkC60UIgr9{$QOM~to(1D{ z*@}HGH1e|;fGz(kO3ASN$!`4q_LmqW7eFC3t*$T7!YK@T09$ z39Y~ClWl3IIQ{`*-9xR@g`L&93~%=y=@E zhRnwY0iLpU^;DS8p1zqIv5uQXYAs!RY$4x4Y;Lu6hUw_{F`$$W0hL2?x>Y*&2z;n@ ztw`7t1{O%|syE>zZMXSqQ$ic#8$*F!=66U&4hxitQyHP`|@#sMktS6wj{Mk_r)uGt_7em63+$wB|A4Wxx||aHOokEYQ;)%X`JiJU*Z|q{#eBf z@|OSVO#1tT-G0!3i<8}*1b}ckiNh5FJI53Z5{G7P_`4C@Bk%u>Y9gB)kyQGi*3?aj($5cOn{?4|!!1wN z)w32V4{q8W6v6n-8C{Qx8J;Ztl=XPL|7bWUdOj(K@Tp+dS-THQfIYL?V)KsHy~0Vk znMVItpUASKOP=cEkk>Dvdc=E^ZjI7XZiMi+ZUyM-FX;5Ux?_XkK(@I!H>?xdYd`xf^*Q)zh|owj+whg$^!X7{KGsj)5-RH~11St8^78 zQe2Nwr(TioS`R0|C@ghS>uFi5&i9$tm8lapTtwqXZ1liP>*|&a=_THXR%`2#8Q@Y| zYr$%y1G~jLgW2?R2_)l*BA;sbyNg*rGVle0ckyF&p-8H)&)CG6F1VyD9WG{{8y!$M zQs=;)(`J!{f5_^rAS919Xm*DJJR&sG!YdHRQ!id!!WR8j2ZI0gney?cf)Ihj_%T1< z7hTd%DP83BRHcN4rucib)O0kM)CiuEGxZVwRHGAe?cjEzHB{Euz9E7}R;Y+GCkrL8 zk&xrb;r}K_iGMrbMlUlyBxSjw^_MOCR>8Rej!1UtV*>+Q$??Ea!#~-}|Elts(z}S5 z^P)!|FuK3mcV*HWchVclb|yaT)905odY+V#8<@&q$hjLX7Q#=s^XmAVK_9#B#rOJq zYm@AwJ&Z{vXm$3(D1l*Ex~i4U&uC35LDj4$mTRwi{D&iL>mw%Ou~~DXw>BOwsZD%) zSeWSpjb^CfeoT~HE!ykndxe%XektzE3jV3z4zL?#b&gx1Wyr~yq~O{P+xI3cT;m@# z2~})-5cwGYzzd)IoMuM{N!WT~kjjI$+8nBw@+7qm92YNh&bVJ4s-hmm=D`^*63)Za zJ;-b`+M}J8L#N>??rMB}iOW}+(bAlgpGf!#ev?pHEq+l*^Z>Dds zDVsgr3EpOR5cM-~d9^#8Wr*9Jb`CTuZr_^cdF`+9HbLqwd#4Z?xG>$3r#W&fsqBmq zRF_7>D+(q?b5yN)JV+a2A(TMPoPSeDoC28CAj6q$!k&vDfX*J7nZ4Dk)8NDp{tX+oD0RHC* zc8k?#AE!VHLEz;@MB&5vQyH0w^|vbEp2tA z|Lp|HaZ!L@WDEp4&;-DMjQwW*56}JIAO=Imt-}h>$YI$ub6ObzA8-dyli8hkwlicP z(i5ATD?~v$M1HCjYw~Xv=qs2i1sg*0r45J?`E;VpK+|Cv*|I$0~@3r9WT)lst-QEQ&clq7wFtx zgGp<0^XHEnXY7>I7Q}iwbh)=9`VA@-+X zwF~1=8fHJbjzx!3&l+*|dOlYDlTOk-?=!OYd}LOOI-K@AdKOYNppaB5IA+>`Rz~S0 z?X1t)cg$Qb+R6HvoAutTBm0*SABeg(WdhinnR0g#f3gm^t@Ku$wnxO!(iykM#1D-) zN|YYp>Sk^nucQ^}m~W+RsP)`vFs9r$N~dO1)JF_;r7oJkfd`tDP+25=DRrZGkP)El z8FFQ&A+pZT+b3#{4s5mI>1)sv!ZT1OBXL~YOHx^FGiw?gTyn--hhbxW0h@b$dTxcd z$a(4;TvG%Mwxa89q3nD9uFU-y&NxU`B_hq(8#Z(uoVgEADS+}e zmbsYIdXnCTH`|{;9%AM&$}I}W&(pRW)aEO@4fP&KZ{LCScef)uABuKXLnA&gmloxW zYTC~-Vl3Sp?3yZAh8B&{=l_IqCsQJD-SZ#JU(E_FRJ4qmvnp5RLI<_dSI%({- zi8WQ-EAIc!wbJ`tq{Xv3meL?BCTlORXE#YSFVgJC!UgS_YNL$G_{j#{499dmPz#uX z!t@1|B|K6HKJPj>5buc_4VpeAt3Ijq^5i9}*rf{mz)-M}J1?$6&P3G2!=H9_o+)m3 zkr52}kC=qNwhR6{A*NFz7dgFF0*kmS+Q=46j+pjd_hoSi&lhIaGYZI< zC=G_v+uM606xn?Bs$5UCO_Y-3M1rXfzN?nVjqmC_qhP9&ruJ@1Q)wU~GFtfHY%Ri> z^kYBnSWjnCE+X4&v(VdtCdX4n#j_xFgM&tocVm77(*rFTacv1`pGLc+IXWs0x-d5i zcF@|mMswX^ezG^L_n6>QHK&I59+kdF#iE9@{XIAJ)eUPs{pf%X#2a{q0r{Fnxo=6Y zm1=HQRL$-zD9EU^5UZ;g86JrBI&w1g%nUslp46Tm%MR7hV`WzA7J*oN2&O{6@pPVs zgPP}g&|E<$DT&XLDWj)3jF_G~v%GxoM$3}RMm2ZvinLN1vyxbIO|I3fJ>M5IWb*YR zz$#61b=9M%!1cGSPoOSBZOgnc+0p7<0>6VJnN=fOoM9ADPxhS)$Xj@rQRAdrM)FJj z-vB^mcqlW3FsngH!L(i7 zx@Z7q3||Y#RGZuJUiQp0X>B#lBFoLnbD-~;Dya42n83bO5$~$I#MAY~r~r$M&%p%KG_S+rb^Uw=Z@4SgjYDWMw_-vc#F={DLucO6o zWTN*xUUyg7>L)ErRCD1iDq_kcrz`j-{yG}W5j9-j$3vGImX%3njxsOt%<`~1WFF=+ z&Ev9*4Ku%#IQDsmBlT!em`~8&y)CnrT<7}g+UvG9ds31rTOUbk_-SePM^)9-d~fU9 zTIUE?I}b=5Qch}%O}$#|@P#gpZw}yM#Pt~DYNpqN7I5P^G&2mG}{SEElp6V zKtI~;IB&?+7vR$qXX1rN!^w?wyLHw$DySWr$)U! zf9FK^{ZEvnrC%3^LiJaM1k=FpWAI)4I3$ws%qS)X{GhYa*%s{Zf5Ad;Twj+dg` znA})e4jPrh9kreheI07?J)_YYJlX;Y1-FIMl^9k+N1$G59&>0eV^yK8pkk_VJpF2%d)99N&C$gEL)Ot3`BPt`gljH_GH2;3?$+<;sU2z6#8l0BXqLsbU-$( z3-!w4p@=*Wr6UozGq3ns+O^XYw9@0ec=n+%qsL$xtjv&`!bd61z=)|eG!IL;mR{Ip zDIg?NcRK6_Ry(QSb|lJ;cezO=GB3TiMben)b!gS_wI=P*+hSWH8z9CMS-w`6OtcZ- z&X(VCruznwyIVr}Ln(856;V3j)p$^|Nw6?x?3}^%)Nu3sy)yn!?g|yegP*nxoC&;Y zDl5P*BSQomtXWJy_0a%;yL*gH8yG-FFj2ELa&9Jy{Q`%C!Mw@NOlla<_4%;oDOrG# z^1M6k?_KaO4evi_tbefA|21vzf8$4V6)y_DO4_<#%-~$;Cu&*SKoZ2%{Xsb136Jai zUI&sX(lhl-eJET<*6;=mRpB7y#B&4tu#?K>#ZNdh_0>81DgC8X(Jk-hGGNl&=6f&1 zS5et2;%$^9W$c*W;&?*&tpEKBh3kYHvM-L%gWQY4+?F8@_h42Rp$imoKSNpgCkmfY z@rOuxSDYO|v419!_&W^r3y}QRTm8S2r9G_yH>;_v}yzU($2&8S2S;NTL!0sk8TLP?IMU~|>!*vjMo`J(04 z6^)Z?xKE52nT1vaAlCTh1xG>%=Ym=f{X4NBgp-~K9f|yg(2;Jp!Ie!nmWl!Cr?I+| zBV5(<%BG90Yzq_p3k3sB@fAj{C@yEMxxGA|rr@nC)VO1n$%v|hY9ZXfo5-`L`P$@*pq=(-Gy z3({%N2b?`@`((m*vESt$s@fxQV{>)%JrNPPxgQQ_94;1F#EHn7vSNt(^@?+O=#+`0 z`wKg>ieC69w7DcwpJ<8~N`>4wBOf!by_Z>iC|mCnI)GWMPqb@VbpDcbO>j3nM{~D8 z$$GLHzkv7(;{Isp39_Xer<+bMN$$Ewx_8=+xy$X*u6A7M39$f6Agbs6@N{^pMTGUP zBh!ph7sCn@Xw-O&Ca!x4Q!5=Qv*d8hBiQU7)^aH1i)VTJr!h|yAAL{L!(>Ap2UF78 zMi{2)@I%4W`ZFZQJS4{gtj$HNZ4Xyz3V%egs_IshCRl$k?@8a(sB9=IhAK8o+_Jxb zMpo?yw%)9k)OpnO8lIeB706joemwaGF=ED46cCb2n@UsE8T+Nbyw^zB=*tjugreHr zz$+!*QnB!n22fwWNy(7U83LQf%Zs3@D?x44D^I?q$>LN$dRbg7m#NuL!=?8a0SJE* zVF#0Yi#hL>p=4jq0ie(cJD}wF)A$`_+S0`OfqQ_x{k(%coog1QN)SEGZ$u4IkD2GQ zUjz}qt2%zL$LN{fsPV{|itZAR&sAg@Qfd0IE;qtBCZd|D(C5|G4YBjU$V5i!w@+Ihd2Fjb(Bsn6gUSgm-8#?bxN} zv`qk?nfO%i8IxJLw6_|eloO$R%=KoU17D~(jMgwNP^bG#*p<5>m{ya-9pH|mYA=R{ zF_(CqZZ)mw+}PzvQ#{ODSpO=L5X<%I60hicNh)@?H0Kvgd$b|_Zv@s4Xy%&tvPm;w zR{I@#5}A7Ca-3ZaVx!X^8W7ea;gMszcwR0pgVq7WNC6^gQ#$$1sV#K7`LD_WwUv2( zZtKi)p|AH8VV-*2>^TwNDWSZg@+ly3&kZD%-5zHH`tDp{@&6HnzQpVLe2M1(hM^MX z7hX6)_15k3D}=Sud^u?nf0DIKpBG*4c;48ApcX84lkEQa zL--E8f~jfDdts&f$?sH%bp2cRLOe7sj#C(MdEZ;ldyvGJcyA3B%FZ25WYLXf80#JN zOT2)0v9KQ<^_O@}Z9t|#0~*7W8heQ+ZPRm!S8#^Hm1EEsS6en=69&Me*SrXW^8q;I ze|wF@GNX4uQErW?*)`^b+<{HOq{6T%_ere*dJ@S#;K)Bp9*$IPuV|((#8&Q44134% z@V8nQ(et;;Z0dPYFBd%RP@<|uGxhOV#7s{tBUqnKUgN(-V=-8$Y$5dair5@-q>djN zIv53-_8R;KkIu8!t^3q4X==(+Htlf@Z+ z2S~XqzI!gxK)}s^CY=^~?SccAA9%w(e6n{)3l~zM`&nxvy?pU>6C1{WzE$I_(XKlrxFWm;JRPwY9$7*as zKa3U_2bOJ*YM9b1WNv8=OqxG9v6?(f<~YPg&l#qs98kq1=*CJBC%{xrg`oz;-kO2Gz0|UZNeN*0_Q(k8u$dCUGd*5 z$@`iv9<<=qoLW(n*WDV~Os6c@aM0*l#9WiJKks+k5iFwS;~%l!>F9jvIiGX1(j}h9uIVrJN*vWXI|WSrf&Y^`{yqE zOFWSD>eIw|Pp5S{74y&Crt3p5fDwcc7X@=Otx2qM(Y>Jj;zj!Jb~C&$dNJXS9_v6h2vd+zALI z-{({}!x{l&VHauTa-ht0apI{|n1a?6J*_wE&FRwy)%p(97djN!FA)^_KL`}N(7rjU zu=*KfGY3W*dDWg8@y96`{)R8(thv~E(Jf|6G0f*$LuQ4Y+@ec`r%La`(;Y(w+P_wZ zx>Wl+f$j#Xc0tlA)eSXEtBEq~U){f%q0|SC3~g*Jk{Ee)0zPEym;t5wM+haE_3X;V zz%6s#Bo9h5BjOB@Aw1PhyZY7R$!XF|_TRyxG1{A(u4>E;VPh2O7{gj!*tG2 zzGjj8dot2R};P{Kxrz60FItBNj^`VfS^!T!!7m}-WaEXI+0M8MN&Jg@i4H_~{; zt=5j{<1@qkQP`&4ZW*q(x#8japYbre9~;o+y}ngs4-a5c>mR|@x<3Un<-h}xp(!1! zWWLVyDF@WK=(OF1x_eYy1T{6D76{X+w7%(qu3tAIPo%ffsaj@6C$pXoh zt*txmQ7c5gUzrV+yrt$r8`T2)!(jRtKe{FvIrvd-}*3JCw{{MPiDKxNM=}hVH ztcfso{hqRob&Y=p$h8kq>7I$!p3XgD=fSe;-%H&NZ3Zhrk|}n5X&R?z+m-7m69jhi zfGek_bhk_X6Wj|qI4`!h#hGO^VO1Vao2*xqS+FnUde@<@GzRZm9E*9nB}hGn3xGgv zD(g%&m0d!mu>^0Y##3~RP)X{esc-pY)IaO9BOXd8Yeb>6&) zodR;_IB+UXjA2;c{afoxmXZnccRXM(ki<+cmHDvx^;ShCQV^}v9f z+vV(KAj#{d4_Sg$v}{t6PmK|!9hT+uN%MSE&#duSysJ*$at>--(Y($TGQs1C(>qy} z?wa+jpY_e!JS(%Q5>3<@0pIB%DC}EJC!vzoQ6Icp`e`R zuPKW8L69L#}qGIdB*p z*yt&sTC~05%gLgzXF0#A{N$TKrmFRY{HbcINDZ~8WeY3B=HW+i6^HHp#@4KTsH&^> z{p1(KhR%)Y-$pn0A$M0 z-qfDJ>;q{hG*!b(CB5QwLzIcjxr1J}#p!B>1dyp%{#0G{I=?Xd0f3ruJ-`NZTsiL# z##_CE8xDs}lg|ls%%{G?^#YE7AEZGuhb*VI`#Ls094NKTiWXUj{?KBhIl=pZm!c2H z=Vrs}le?!&k?^C^F@x3Z{2d0`ZO-fvrO#E`%u)Ssn8!I;occ3#@?}x1do$INj1`~J z%}!~{2z}$~;@ip;()x~{Q(EMx>As7ePSNg^L;uJnBrKKcxi4+bcmQBM zN%hOH(+H0l+|f8x&nOHs3PxVy0p`Ds1+sJf4xr;3F_y%jO;vuG;h7Lw0E%r>#Ehcl zJC9rHp)>z10se4)|L@4jU%%vk!&Vd$U;9j-I#(Xno0c{D8C_Vc|6h1r{^?y);N-~vWBcbGJCat&GCHKRHtgc%BK)v$ zTrc%OPLJ)WfaD7X>79=gW$u>Pf3raSs|3b>`hWjlUII%PEmn^~&ExYjjUR>l(r1{p zVQ@yDxdo7YBQl_M|Dnby1`>F$t;V6b#aYT4cDldanC&Vi9lW%t+Jt76>sb*Qd5H+? zwrP?RR-9eWKbSn2nwFv%8_?o*9}U=JR&*_NFA!nqYD#H@J708_FPo`;8Rz;mH~)T2 zo+z0pRxTM&BST8!mJ`1bLJB_6Sh8+cUE3VIn6E}@a>tHS?Yhg&Bf_l1W)bT4rE#QR zHv$k2Mn^AbgCr(M`?{Qy2TRA&Z4fs0L2$v1KvB-)IEqQP>U*!-=$WK!46|p1_H^#k z*0Z@S)$%O6e~+Z=0*~wRFb1Z7Q@fjSEnAaE;l*F zm6BHY<Gn8=h;+6b&HZODfz}~u@ z$)luCath10Vrm*HnT0p=6TT>$*{5b(mR#l~3xbJAE@SEPgnRpw^KFf5T zG?6C{9N7mMz%X)wQWBOh4D;E>okp8ay;~j?2vt5x`)t-zr)4Z=e*2d!~&}f=u1OO8D_@6cc5J4}|{{Dpwkvhp`}cFxXg z`R~hp-<%}S*4dWIGaq{|_j*XvH!drl9}mMX@s^S`Y^e?$38md-F)hmmV$gTM%~V>J$rd^|Fq-b&NR2GC-OBfY2~$&HKtAA9@=^${dVX!FmqprOXq!KwmS!P5-O*h!whL+Sv@Mb6(a6|Fk%3GtW`&wHIG`xA%~ON zPOftmRGQXLi}_6sXWw}xF~QO5Q7ZfO(e24DVTDI)HO^-G^gU53K^)oc{1amC;X!pe z&ZGOe66hzM|Jk$u?>kC#-SX)Lnh*PU6{yve(ct#qBmV!ByZ&1N{V&)aQW(SXop)dH^wP(Kx&Z{0hNy2|@Ekbt`S@Md zNg$Uv5bP2>Y-ul8=Z*UYr#QRk>?@%=Iy2Uy|7k$kP>tQL(B4KXXWd0xf%V02N+=Z!dh7sVUb+ zICzhNu*ejtd2Eapm|12xXUlOr^ULeB6zy>wXcW?&AlvJhKjrz=DZXWJkCHOr{j*5EL!;wy7Lk12;8`Ck;AF}TZa-w z?Yu|r)(d&_^{VkA^`+dFhNVq*Oj%qc%Fc|oCov_)=IvG2G==Fo97M~y?Y$ez8ZF88 zzeuC}_awXv*nia6syX>A8Qju|^vvzkw!L30mR~R*%ZxwSw&rD-p!6b>T`zdUxRqPA z5@EcU$4grRnvB0~+&%cgrLAp3g`Z1HQ%R`X`*;XYVz0lPI}79k>mQ8&yvt1`av(w3 z!R=plrbiLqnOhr6<8vTB{)WxQxKS z22O`}>ywn$uXFrcWxA+D8ci3AMYRwdcN=C@J3~6K{CJ}{-h?*5h~7?%>KDQ_tiN^}*k7tbpKfc#Lhh-D^YC^eGc zIFc2i3Hr^rb6=IR_;w#%Xwb9l=-Z}hH+7zHW)~F3NQ|$&Ac}pxCr$88D04Sx%F3B% z3P9DEtR~>T`k^NE5hcF}Gshg4a)4KYPMO@gW?aHu8Q@X`k(i9*>!$iyX{ z=SxiT7}r@I)^G34F|vdZ6U>NR9XZSfsK&4<78Vgl+xB>4@GMyWEGOI1P%u!Ak_<_oFQg zVn(-Vu8YoPdhQ`h7B1;+_qD;!vdsHK(mN9sKBu?P1T03pT>|53K3$7tdMDy}YV)_Es~8TB1b zK=kFZ=*xF-5L$|X@$l~<`w*tPOU9s(8(&kgv$Mp!jJdCk865`<>%9|SIrGI`;?a2E zZtp-D5}I0s(fU(m`Cp3SCZ(VLrad}P7&%bpF4qX??v(a8JVrD<39URKu)b{(vTl;b z=}(}BGXd3#bb>lp_@Q{%9*t<3i3LWHF7fPIU9HDMnx*GyeDp0t1e8x1VFp2V$>oR$ z=8r}@9XLJ+c-XoAAodL?VsFtnu&me`a23h($jH0sS=ZHJ{Pz8@mt zjEkvx6a(T}yTkQ%y28?OgUalMz}XL$CC3ha|JBI+cPjX=75V?F3G5f7=dbsozutPD zK(lZ%rk!LLDA!9oOu7~-0W))v;0Zq4PQ=M9S{~0};@vLsgy}BW@_0)IEIeTL$0t7r z&SJ@HMg4B^#d^Pae%Pg;7!~MHkmWD!CZu(BUVm@;YyH;~$3AFRP0x@?YlHctTkO1R zp7(V>=o17nxKj$<^JxHmlecd`%p6=>RS9(iECXJDX*jm04?!03N=lv94V*7vq4*~2!S6gg@3w9bK1)&O_*48!1 z*YF`rr>bBEAlbbOPr~tb&ST0$p@M>KMgOz)pWs(3<#09FE@S#J%NjWay#R=&jC?!AkcM$B z+HpQSaG1Vj1Y!)O`EW-J0eZq-Vn_KhqmOM%S=ufA?DNIL+}89uhdayWr`_26<)epv zV4K&N(7uBUx@y?g)sOCNdrr+C^6EOxL;_E*}^3(4DQSiW7 z#0AMIKuXvjoBL>7bdFfAJ%aZ?yEnWhC9L z?q;HWSZq(9X1ptU{}>s8x{!)I00NvMT8993Ql+C=7E{-}^Rdib-OtJgcdB)eLcPCS zCL`W=*i}2sw+U89UId*L^GxV?O^^rS**Pb;#3hhD-GHF&m#~2+xee= zTwee6D)>J}{PnV^4rB6e<(WzKPcC#&j+kGlmsse&#**J1SK>2bpcCTtn2X*_#NJRy zV0sKc_Q%p03c1W`evl}Pd{jk(Nj|VmxQT~AvCcESxgHk7?yAe>A}xQfuUf4J`KaaH zQAN2Gn0upaK9Axk^YF)2G?hEUc3~?+{Efk)q3|9+gK`_uyTSQpJ=;Jelr!eKH;Ee! zc;*MLHPTiDZSrW8?=_sOFm%))sr@=B5zM@2W_J%ajSBRR6gABddufmXlYEP{RXjc? zL#JCn>2`kOnpA7qW27A$mHyQJX~0{o{sKz#0(noed{Vk;FHpqcp#-Vmi`J=aT-I!V z|9JznA)=O~cCGNZLi8Zww7&k_vJ{=}h#J(>CwuL2`0~rQmk0dPBC7NiU%8_4vwEi| zRLVybWj4$Dn~I1-XtXEf*ci0&;r%tS9ab}1U$O zz<%EuUR6SUC!qX67O!*0r|F$a`|Wgx52(f!+*zE%-Tx1FZvhlnpY4m101*NqxI?hu z5S*Y%a0pH#!QCOaG|&VRAh^2(hsK>oli(g)8VRnAL!FYP^6t5 zEM_2n;AKW@uj#2i2LT%CFIE35xA6B>9Sgv=r^b-c-_vc*$bh(wbCTaNCa7qEhqp&J zKq>hq1Hcc}epdn+V02Td9dFH-Ug$9iCcmJr3 z;pviPea;j~^qM;JEx?aVbU?P6qJa!KjPs^3DpZeH38H<%AvCu0U#R(d}wt&Id zizXi?CglyPSDimB^@U#v4F7uRo&3evw}GA_-kK=9jKfG8M?N?45dg?WR-7b)SPwatm9M-OUz=m(aId?w+k!Yaw_jRzWU*oJ} zQjM;omYTk9M^_wOGflk?-h~;JAm7UcnK2doR@qSFM|UA#b5J5@rL`jN2ZY3op|X84 zU$f6ysBCj_kI?SzV=s;0d>GO>$*fn&`5+_UU02 zaKVT$0=n^~Jyf0l0%U^8>Bjtb6aezv3E)feT>PhjR6(_k%c(J-49(n&pa-OQoGK`~ zR&dRYB4YejwDdoBu(;AlHm_)d@}kaB{^x29 z<-MzZ;6yo#QkL2vgVfEgNTfr48VC#?A=jjkr|!fH;5#P)GZg&`vG#Vj3y-g8%cI+6 zRL~A~`|JtfkGPZJ7Ti?G^^-zaJD#s*aYuSVLu>N&lJAxTqNZ~`VVzn7N#1YcNK4T@lX3g;-C!tWCzx250YCy+cP+d*W{E!$mCY$L@gWB2jz z9wfm|i%t;{@JM6Jy<+b|6Dw?Ot%D=1+B=$~o-ilW_S3V{G51?~(1K)B z8SYI?Um!_bfy`v|L05WAO=R(Wm`^fO zZ*-~g3WC}cfSkAF2%bO5>evZi?AVW5L)D)jp4Cqoot_E4ENt)4x~q{L@!5ZsoRSYiDf??G0Z)CQ@6s)<%i& z37$;Q>@r0k-xZ?y;kHG!O$1Z$%BSZlG;7s9VN~L!S<27wZ+f9jrmI6ip<K914u3 zhyN>QE0;~ z^(k{6wmc@FCb}*jK2(n2P?9(6JCgDbNo1E@TaGsTJtgGts<(ev1T3$#)-F(Fv@!zr z+AOK^s%(miWwpi6wMv6pct)kACB>14HJ8i_rPj$(R7qLVeTyO7qW0QQak_BraE&@A zikez|)Y;_0zW?~q7xLDqE;fM#Em}}Y*!?$$oF^kU!|4I@Ft7u-6oDC5qIy5<{?G`B z$o6&VOQPq_&)x7LwYoozPM3Ru>vQQ4kpDs!1*@W~?-j5ZP z!w2k^@WR10o;i*pmB;`!Q=o2iUFA0QUG#VJgBA0T(h_km5Z5$3b)l&iUvkY1FC0E# z(Caxh5p5LwFjvqeNT9>Ovhv*q2hmD?U;(@1B5eFj6G`>Pmd$*N?2ykT93f*~lwY>sP3a)^ZtP3EIz2DW+qK_$;w3@{gL@Ip_H)i+A8TA< zF9eTi&cH83+<%p709fRsuO$N;9k5CzBi4iHm6T={sI7SK=t|M&bwB1Z*=D4o)8CCx zpLmF@{4Ii#0BVP&cMp*>m%xUb>{~OcyKU4Su&$!;?$%L|N}g`@Q=Hy3i$EFbM6wg-qsX!HQz-trDf72Rf0k*h)j>_-2N$PN-2Cyc>_ zVp^`NJU)kJD+9iZcj2;spphJO>|^spmmLQ26NX8!Tb;gEh~9-R-Q|si7*3sR<6ZI2 z>bZKKS*}w!F^Uc=rq3T+u;dq%hKzV2|P^y)j8u0j`gy_U+PK(}x^i{D)7enY7N6 zlKc`JDn9o(mOxdr{6xAq)*^Dpx^afSfvs`Ll83Q}F~I{zBpNo%77=nXNnQ4Yr5PrV zENG)`u=6RSnjdgF^r*HfZY<$EZT74w+eUI?N@XMHP!njZ~-gjm)&V;N3 zW{%@p-9~xlhp9)8NY6)v2WXmiK%3Oo9?$QYrk+tVSAvEOY8#b#*w?SH zpDIV;)W?P3WM%nS$vJ!L&Vxc|KrGb!(3l@x&lzTocFYblNft7PMa=cqZ-P5ru_+t| z;ETMpPcZg%*p(O1GdZL&(y)%6pC@0mc2_U8&k+^wVaF}~u>1)6$Rkx(7ACYqL?qs{ zHsK&Z!=fA2zfodsYs;OYA;nW)E%QE`=2pR~=<7`pr4(yXL@vR16Q|0WL5FO&R(PY> z{B)1J0GtcAH&$Ami!4mK3`J#+RqmDHykigl9ND&!_z5YO+$em@@FJ2a7+pMet3ZzF zr&Ug4(GK##_|W0|e!7Yu7W6xQGfhXQHT7Z&o#F!FEWrcg@qM~zQP$aaW(STJK5)ne z$6lrrLok?Q%$xK2WHFR^d$7G*Ec50y2RCp zx=JluxxBoQeO+k8A3w6eqnlA6Ukqf=Ku=yhj$>vqD=w?Z+-2P)taH{Fm)uO#GOf;i z-hqbuA?vWs5qz)(a}p^S{nE6g!Z2P1iV%&f_NI`sjjbp&m6HaIpzX@TFW?*%1EY@B zLDB`Sij>Alro{oQk;=M}X!pyy)|iSYmyQ-%qRARx&b7A5NRrhRTlOI4<&!YqSrDEu z)O@FqCUj9M?g@u#dE)hiK9T}G5Lp&-<>E$Uz-pbqt;rOY6X=pQ6qxzk)z||Cn#ARV zpuv|QDGWN+Pw7rv_AO!u$4!Wa$2f@FT*Y9bvZ${CUHGq8l?c&vQ$r`a(r0Fkj@Rr* zZ}~0`Q9IjrNTV2Fc7g2r=(XlNWM0(I1(^ofu-cTkIs~eO(#rAL*~(n%ctC<{+~})G zcy+DIW>Y;?YCyzXl11)asI$aRq$WwnA@y2f$LYNNQ&5%M%1nu20^!-&S&X!jA_m{& zPnqgBDk|iX7~IdtW4j4W)cI7|(a>-K$E79mplw|8e?{&sCXZIS{Vj6OQ0%d!Gz|6O zc6h<>i@dz<+@h!Ml!yw{q(4?ytxlLrY$n0m%kDy`^h7`)#MkLnO=U_}yh>Vo%EZ8O zRdvN}h~YNZLFAW)2Ejh3&o@r1ixS!n>{Cwuw>blr^uaqW9Yv{h)^>K;9Fc zQAfMnA@&Sp&1X6<@Azz)u!r9MupfCaw0ys)sSzRm{fE=yj9NYOv0tXtB2!IT{0rkA zP4lOxNmdT_!<6jfH&o#Kq@^p@rNy0A?xyz0yj{PLhEoW(Psav7j<$pS-C-MVUFz^v z*0NW>00=}qvwY_#3kvn}SQIV<5{G2AhR-berH(>KudC9EO)3%f?O zS+*$dqxG65?k&DvgouK({Q4Kq@2sX_1`|O$)QbxS2C`FLMiCOsU|!FQy?yq~2W;Mx zrZ-!a&NXMtIu@+EhQ+HTY}ULXy_6g}XFr_tH1`&&k4wwPj0cIP)@9PUJK^CwRqZLqK_qthG6VbJ(f`XHqYTe#B(>?t89Rec*g?&-;v zNCAkg<3PVuWK6}S?6i&#_13&@Mxu9|i4yfbT$l3cpiD%}35S%DO9cf42s5}@^YlHW zZn|_h8Z>v`JeVc*8&28wDIXQbWg4e>?yqoI7D4!mthfrHGHpct2U^IMN5ybSIYBQ7 zX1BW)%(6y!^m9_qAl|SIoD&U28Qlh6QM6qf*|_^l6gJO?)U-30prj8(M}QFHmcgpM zhH2-UCydoRDU{Uk?@p-NWS3Po){>Q@)+w(YX4Se{d67D|@?H0X$mQ;X&4sq%8c#R) z){{>^4iBmjSv@Y%b@L z7&wpdKcR?!VDxw;airAWr~y)P9zIt|9`kAT?~;}lNrOSraM zcCI4+_E7@Kw93a*4wtr-f&4fOj%>MkZkEA0L|Dbz+9I#(5R;PDeim0kYRtI`9@#6P!!!F=OI%g zMG`HIccOz_R~x{cE6PpV)9`ZEyS&7xp$ca2fhz_~_R&im-jaEn1WotK7ZH35!b4*Gl?O zJ~FOi61hT7#Sy1=E{LZ{*sS+jchGbr%LeWVH`sHoO53ybgS&>2iVA2|gTudI3!WW3 zt)Wtw;GuayB9EU3(q+EkD(YwzVx^>}!zQNrAg*F_Xq!{}9F7-R4m(a1N(=h}U0a4% z4W&SLew9C72`$xlfa{zerwdNr2f*GC)9Tp^uYC`{Z^I@qiGz(A8cFbzZwKQz0M4r# zs1>%Q5}fcd7*{PnRsY1Q$*9>=F>~~TUHOlnvhJ20I*uxnaJd@jCjo&vp08eZ08KYy(dpGQ!|JbOZh~`(=e&-^Yu#kAJ&JYlD z^huCzY5If{(V&Ekxrk3}!1=*eVX$$j^@A*zKg^qW-rI@Oj2_dfHOWJpXo@sqGR!SK zjh&1B!ZW*ZczRq;&~IL*IqnrE!xwVvyvENT`?&S;6*MOlcL{fA{LWp=@cvE7(SJ$p z{9k>Jj=vjL`8W8_EJ>$iHOYG;ij+2TBhALZEQ~o%{X~&H>a8rL^Lq0zv!pnB&McEz$=k|p;-NkMulvu|bkB4eJ*Dthk&j7BxH5@?QmShXW4KfzMvkTFcA0f0sRSn!aEcUv0iVRJ>sS7W`gVvD*?i)S9f8-zgru*wSbz(M!Crl- z^teQftwp;xHnAq-d}w^8FHN;ubGakd4SPTghG0Ph2J}l1*}VG#T1hTK@yM*792HAi z+F&vh%p9Evb*V%9ZNqx*vKtP{Es$iD1=NO7%5@4`xRY4x_3k#-~7H z(SE`!{TH@L(f0ep72_%)Bc{(kPz#6rM0~9*<|d@RaETUh#YgB5H!eD4#5Or3YYA2; z)ps(5nr6*!(AU7rzSx0N=p5Hm>uRmp7Dz{o9K3@}=5a0tJuwe?<_*8Q+J9AgPh40)Kuz!5RTr`;IaRGyi?}5Q#%rVKbcSLX$;85-7AzOrhpj}K%`#60P zlfabXRg7B(Rd>^gjB9Q>KT%PIeoL{m6>F2%q>p(*Kw12sEZu?R{w=`Lac;E_lUKbE zY-)V;3t;KyWqwgVlq~gm`DzE&eoDQfNElm?HA7MLoTrn;f@#PuGbt~0vP|sF_R{!- z#+Zi2*tk@T`uH0hem=ZN?oJg9J3?-mj!#j1Xt>*A8;J7yw#53j%zQLL#vljeJ7SNWCV}k*tQiYAN00uL-uYk6C3| z_5H~tar=-5K>c*FcMTxXl^Uq?hb>&$hC4I|i*~=s;BE4K|A5_!>EV?|EyTAC<0U;X z4Jh=-VR?L;=Zv<~tp|CtnwaI&Z2$A9jd_Z1#=Au={Ze*HY`omui}R?xMP7tNc-xa| z=rxkI?s|{vQ{TA<*wZmHJFcMr;6cAE47qu~!|vd&x9NmnW3y~ocmLuE6CZ(=l@X)b z?$e4II$@x{tnm5+Ey9l`eBlf}5Mu5n$jIb$kab+lcbT0FW}1tpR2hS zKYr;ZByBaa{MGPc5AMY|(rcFU2K$*KmVJVnMm$#WmsH%+nPxpTw^3a)&qRPF>OlyL zI_~tlSKt=y+Hs$I3zK8)`PIh`ZG6zv3UL~rk!}R+ZvzW=deR*H?EJJ0c&XUVJzs#z zp5De+{qm-I`&ekPWrh&X>}95e!B01#p$apXgLh``#40acuc(q^dL8wr4a2#m^%592 zvT9!KQ9TYFUS$*mY>{aNmm0@C=}!{j;|QFI)%BwRfox*qg7Us8p)9Klbm z5r`&F3FD28d9dfb<|D#H&C6RowXw>GF0+^Y3{K6!5a`X#%lMyqEgn{aSyq=`DzIoffwoIk0?%TuJpl%$kLG{EzyV8yM z0-|H@xH&sF(y*UFgN(OuR9VUHsp{xDC55!1a8r7>s+SLB(PO$dN>Ixj;ekf;Sz`WT z93^B4z&lx3t;0(;Pxt zYV=%vS5FK=YK>SJ;stm=C?@1urs?%a)jR-(9ZFy028r+_uQ!aQlT>>?nA`TRyDSRd z6K@&aADLgKU~+dbSW%hRi{{mCtQKjszIZ^=_^YsF9@fau!>{|bG)x7%lAbxz&#-L_ zvFBJjoB5`;S`WRvVe#qYypWkL;{&>nP|N#8jCyKu{WH^AGb-A#{p%;*t4R+tdE7IZueIpIkb67T>si{F8_Pw2$9MP1$FB%7BG3A#V$lZU9jFE6h zM0>R%TjCr!Jo=ZGrgbe+e+Z=HJyYNpQ4f1Uq^8c#i%G=rG#3JHRBDF9l`VqpW|M5z zq$1sMls9!o5{XKl-&Yc1X81*2R#JRJQU-CQ;8!uV@ALL`&@*ydWtlv&VI|U8H~qq4 zaj%IOuZI(L^JaeGgU=SR?A*`ndcmaSCEy;lO3FHw@glJ_FX55hHsf>DumcO@s$C%Z zlb#D<-GGkf7+LBD93Rb_8NI*OVbJK`Lpy!)X7UY4%-xq1{Vbb1s5(3hAeIiToDwdM zoGrPFLc-pK>F)z zAN)xM^?z74cKqG2&>7jKUcie+1g%WXrmD{7$hj2B-p9hJKG9&CyfoaOgQGh~uc&)) z8Cvq{^*Rm5&yuk$?+bFOpvTXq(#G`2?8Oxo1x$ugCmNa~LA${nD>BRC{_4I@p_l!j zMksB;rNoJZop0nN;^ch`LA?zRxbiWOj8F(6?VD_$C}KU7?g{21#r^A(m&Ox@Rhm$dMC-0-~cCT3xq@ zXgI378SH3~&^(wAv+b@Jwzp;$K={w=^Q+wuhqHDx(gwyOZhc1$HPjy(DLJ|CELAJ< z@%^PI+qWOu|{mVY?CPL&`5t#yXHkr+_U>Yq6;oauINm`kT6 zUR=P}(l+B(_tzb;`2dFM0L zX=5q$zFdy|bvK*(y~>U=HlVyZ-4{GW1;Q#(dB_pm^CTn~7%LVG@XY3d66cj=U4NkI zM>}=wG|YGGLusyYDYX>420qwk%)16Fj`31+*6Be&9P7v37v{ zhT8(7onpwgc%)I6a`C=c%bYtn-o-(5X`xHaVIdN)c;u(1=~7`$yDej2R$Nv!U2(B6 zou?&;`J?^N6XsXiNU4jw1#tr;>tX7v_=H0bL@rLV_4%UI5lNg@w1P|=rst1Z-g^aa ze^xQR32Z;2yc!Vcc=4vz9)4cGahn|5o?>8*6-uKaLdyz!j;5Ted=D+wlKR?cn)FYy z&`IIj&%~@zdGxW+z_8%B7iopiTzLi^x5#x6}Eh~&4AK|Q#3=5M6jBeNIC{rMx_u8=(z_W0FcH;1HYfN-gK6~>U z5TpZ69;qDqFrlBAa%<9PXzXZ6+urrnCCSGjW=q2B8eNtpV(dBi{5==)ofs$zJGqDiv0`o$HFH2Pb6@ zRyFGm<2?hf_hGtcfoJtG`fXxTo*OC{Iwh=c_@4(!PJOIY_>F%4mq+SG;CRsJlkss8 zPFL=+bXGJ)oA!)=B2#bI=Uq2X5Mi*(tArBRok>}NJw<6Wv+NWnhqetrIlEDnfUbJ=ijBRm`BNW*UPcu4v_-a`=u&>RhhfFGwTQE34eV#w6lbo-y!o`R zG?$PY%r^hXW}4PG09>**G5F2h7WH4vu={^!5&C-@<$o+Qz7xPS4O5COmwlSe{$sG$ zKGk5_+|8buFXyh`Xamx6c0kL-lX!k~tzGIiy3fRA*0){Q)@R>Rn@n4-wv$gePUc!y zPW(dHp(!fDu=6VDG#W)Xc$95_`*4;Dc1!{V&y*g$5=9P1Sot^%oG*0HR)H-$Y^XC3 zOR?E_(~BSFYoI&4{UD__U_z$!m-6lzVC09a;L&ZoSq}1P$+^t?tzG_w8D@K40SPiG=+g@?@jyhySdJrq6rd7_84U|P{)`S&n z5MS<;(3NCKDz42WsLoX8t2Cb-zM;PQcHn@>>_w=@n_e({kd9k;C1e$8%d1{CME89H z`YMS*gVc(h@3i=&n0f#(g6zrUPYmYB1O`Tv$tp$nPDsj1%VNDzQo4^3Mw&;gDNS?8 zw5!6D?Y=gBoeZ>#fBN!10wneSvS6#+o&=d0E7;iqZ?2239Mm7e906 zug@vD@kRalRTh}ITrN$eM{p&HIO&O8UiCl@l48wIBK`T*s_)y^)n z<>wEV`+gw4Y%2{l6%BcDfvkj7VlPdeJ_fC~-_WK_xNeA$*+`8vv5}1|?aj7}WVLct8{=b-xQ|f4|7|RkiM*L(}~87#d=EqTmAT zvKn6F5i=(rN30LidfWV}Qnc;ng^7p*d*4N=FP~Ovuq^z&Bn-Wv~ z#G$s4&339yhT?4|25h}KJ)|Z@VXQu0d4Z4U<#K`vbymZeCmEc3$XkMGDAp-}L?` z+{(3EzXc$7%kD_!4>U>(AiT}#4>Uq#d;Lr+0bm{`gX{vIWH->2x*uZvfp*+ube#nJ zQTdJ5eV4d(PDT}U+-m$*i~M>2gSP+A?H?yB4AWtEn}A7-Gp*gZ+TBBS0H0WrkUed_ z2?%7SdJnJbkqJ(BIPX*Pw>V9|aQ7}t`1kx+{AT_`^8SE`$_eM`ZvJArLc3Cn2ERQH zjQayKiXiJNPmmTv0oK^Wbjn<+fo2JS{J+aWloE>BrWWs}G%^`GMKnNoV=ES0!rnt$ z6U}RCHhJ)R@+ajBg-oJyOamV!4a9zZ6neiH?cQOaZ|?!Fs3ZL1xM|tdadk$0p>IgK z3yP`plQjejnui6w4-J5#p}9}tWG7r%cG|+r5FSsY1BFy^$0M>onaAbVQnPo(b;Zdu z6&fA-1E6_Uo-<_Y@V81@ydP3Q30yBqGHXCGBQNBq9~nfu0r#345EhS+0#*cSSa+LX z1imbvNUcjZyK;>*yZ1t_JY}dPeO|e$g@wrn+2=Q{zB?l?`ze2(T5i^qVConsKcasg zNc}Y^Mi%6+w=sv87jIHyJ*ygoA@$5FkZ+y3Qlz{lac-n}_ElL%PZ49voSrsIOtCS# zrndP@O(J*Km#TzhR;eigXj{v!VhxL2jiZjRhdv9NJdWDw91$n5@7@;mVe;fbR6|{B zGZm(;iIEL+!?#lnbF#T3%Pz1meiUJpCx|94K=}O)`5DDOH9gy}mRg?N^lFEXuGsaU zdzx5%*>;u3O|&S$_MIeq1X{a+YRlTOi0W7Fcs%%C639Irde`=7X>a=a>mO*B8g0Is zG<~O=Gcl3wCC5vFnYh#Pw&I{OkFOo8G4vhRg9n|3(daXyc2|IpKrreQWq4&cxIM4D zxoF^U+_~PcJZir<*ZlI5xp7l_EyVc1o}tCn!M9*fDLxijm_OiH(NdC>Rm2e%_8xLb z%^Hsw?C{&u}+SbZFXP8_?=0B|Hv+8fll>j8eA;)8PB_ z?W=@HkPEA<9_mhR>|Mypo}-?a7@wqGea#k0*Vn{Y=e!Y04LI__H*8wA?@2zNoP({LB)s{Dk9X@!RG? zw%BokEAZ{Z_$q%7D{%U+26E241C?+Zn#Mybhlr)0EDhI%EigUTeRAreoe_AiTIVf) zL!}>}8b8c>!h}JWmcNn(Z8I8_Rw|v^^RZEGutF(87{L@*pEmr}{y^h?+P6#UFEZ(r z5%S(wV1AK=bES0gCfw#%JN0HuwCe%;#o5BS9@v_^d@LIw5qA^zaXXnvuIXBoQJ97y znxS%S=Hu>3+f0%0zwFy{8&644<{oya89h%R#LyYs-OmLzsI z5gn()&VQh3jY>tkxp^$X)0v&{wB!0Sl4ka-dQFb&>)b(2@7mA0#wVv79VQ6*#;3;} z3j4a%RHQLJ)J^fmWJ%vsTw&q^Qw=UO_Dg0+FAk5v<}X}vT6ttn^zV%_B=8^a%#NCFisCuBFZQ$b+A{&07qlz$BPKb zjh7dEq4fz`%lBGHe8DD?+BXSuw0*b9WV&Xy-r*}R%1b@WrqYUjk8U0i_N1Yb#sUwk1cPQJ62fT3fqzODG8 zGqGVw6& zSGxdH28tOZCT(QMt4<7jKEIlN#)AH^`Ca$?m|4EMwDpp{2yB9K*TZZfFH063ic>6 zALiBtzlxK8r`B!SBK_l$62TAYmd6YhZgsXlg7I7>_{Bzgod$lYcn{!9+T(8^485rZ zq@JD_P+X>W5jog9oYMz^OnJU8-r$lM1yn5PvDvDwpNMS;%)}cTJM$5qDmAcFCpd53 z&{oIv1dHl>A-b{M7=bRIFPbh1Z+SK-{ffZ8W0ngR9(SewB(pcmun(bCB^5C(k8Qf; z^|F{SK+>bcuEdS~17Dbqj9zY*(Plc_V0y-SQ|Us-{>sEr`s}3Tp3C^F5U1nE{0aSz z?|rD=oqXcsP%bkncOf03(cj1B)aD4u8!)-hn(AIJq2Y@3EU4`LRm{T1lDYzoW3pzY zwP4gui2Y8V?MHGk^8qd(;Rr*wSoE|L@>CRy>v^0f_$)52+m6tMmUTm3TRp$z`2I9_Q(1j1|bw^57GCMu3kV#9QF-_VB3V>Kw4n zRib)2rj-ot zAV5E#rv(!;psjqgUMszQ3PKy)>)M#6%BuAWuaCM;U5STxf&hWMC}wD`NfYWX&?S@h zV|_iUOg$tl|CclTK?hS*_uE7=;cIp`{81`CS8b4_JYgb%QTKg$k)BAtH08txo4w0T zM|+D(7|$L(59EBb{>=XqO`kY`$R7fQ>w7=|{v)HHWKm);&))3;iKC+dvPE3+w(Rn5 zn)E4b_6(%!aNbt(K_s}PQBXk8Zl!vM^KEl`wxfH!RQRf7p%Lkw^2ah^`=dQ=vALS& z@C~impjFxKrINkO<#x{{ETzaQA4pl1&Qvj6cYE6uUe}WDy2kt!I<)WOkdweYeL#uQ zHHjxa8r3l38KTi{a&LX+MY9u9YPpU@Bx*3Wz~GbkGx);g|{lDueFUi@%k z(Whx;#{X{m`d=<5f9eeX_p33$7XDM2_1|pxJ1_Rndncvs%&3>X&Igbf{uVQqwh)bT z)UU-e5w6F4d~*(K>0k5B0q8!srr>=tjrjVgl9Cjw#RFCoiNx=k^h>?(9jVliC(f7Y zDkn5+(ZlWCC=(I%f_B*pu70OY!4qD0C$rRfuRG+_p~g?$W=Pa5mVZOI*J@#LxZOTZ z=ZyC*6-*yrxR1LVWd2}%|6IkevbI=QqOe_je_Qx8x4EcN*wb_ZG`VgzFJ@I+MZx6M zpj;=5oYOr9dj!$QgOVz_S&E^FhPfW9<{qLqGlskE;#_0B$DLPUx09DGD%hHO=<88W z0Gf*1Fe@*RPlp+4xzjg()-f1C$o`$7#zSCfzNG?WV9ko`Lb!97b#OKao!j~EOK4r1hj70pILI7Qt~;!zW%`SWTLdoUTJJa%}WY-U1|k`zo;^7Cs#!rg!@ zrkwCQ;r;d1iV%0R9t`;<#ukZl16OABICM`dy=byO^cA&TEHdm?fZJ zQAwUGDo|CD@WV~{qVMk%rq^bT4d^0K;t#H6TvVTQV zgQWgd?i*#r1sgW-Gwa2EVH1g|fb(*qmq-*(mYkK24zO5)(9pz2s0U7oPlvO;Dik-x zjoSn%hL?6$6=|`W(|mv)y$cfMFom{IGVEt6y9DEEnvtE{8sR0 z>Xv}uZWD!7`-0}?`voBU=z)AHP#jckT3$6&L5of8dhd?!==XEHFhjUeI&bUgZU6al zkNDuQu?zofTc?p?2wX>a$l~3ZH*={VHJBo-*ar)$9aV_cRP9cLPcI`GnDNYSiCddG z6EsYCrhy_xG7U7B59&^foZ{h{8w)t@mCoL*F{TWwsa~jnG$&-WE^mW<-oxnqwi-^t zC0QpXSw}V4tNTRfwHT64ST$6?i-DeKg_N3Jj4J<|m73i|g3VfG|AdG_D2%l*E;AhZ zWi8G6TvXQ#!ETt4S6! ztg+9yHfX=DDQP5oi}459Pxyd1#Btc<$pC^O%0Jo4MK}6-Yo5#^$(vd?-zqUuk((j@ zl!>W;g{4BMOFnMNcNQKWBpD|-A^i` z`loL*!`_ve(8b{#_zf*B=r4Tl6TeuL{IKUt-0gt>(o8dI&Amo`%yqsdhwVAx}X+Po7hkS19)^ z9mzAex)ZM$M-9}dZ?M>oP3Z*esz-X6jJ9vv&~(qdwDlA20n_-&cXO#6khos^?{UaW zWuFkV5qw4>=r5bkRf^=r5Q+Qmrs4fmgVi1$=#HJF#n5b5ONK#y{^&5Wl3;vLX;@Qh z4jl_kOzjSOI(QcA>0_o!@`(^#;U@%bJH>^48>PEn=iT|bKX|?Wb)qmdE_1xjf`MST z-1q4!tL`oPQomOpSnJ()0-(n=d+o-m6I3E+4@V68FTv$40!<;0nON@YK) z7~1u7T6^8P+G|%dSS{B#)18BeU(8bamwHT_-#ayisYomeme`cyUqEK>ZnZ=@r+BG} zo3`QYH2j0o-8st0V4}ufxnB(133>H2wlFEL(qGF#w)hj@UigTw!xB8MWNrMo-a{Zt zM%_fq00=sxPRq@z$--)uUo9zs2D66QrZC8VEiTF=*0BoK8Dsz?w-2<`CDEM^+Vnr{ zNp!gX4+et&H$}?7SJ(Vq7{(L`&>rE&WilQ^uFQO4i+)?N!IXj~Zp$WaQ9bZq*{dzf z^&;A42L>(G9u1ggbZ&j*NZ(IW+&;&%w|+{V_ful4d!Y>v6(H+dRW6yXw-w^prmpAw z?m&I$>oZ4H-?HcObbP&3u*hdZaS=RH%ffrTIRJ8u`c@iOGCVjk(iP`)Xa$Ytj9MB! zKqc>s^=8c7pf@&m_#P})uZtwx*#pFFy`aDv{0B=?mPW=86PGENs7h4MCkG^+tQD<_ zDDahShuv1ZbjaRhye;u8f2G5d-syVVcX_DCs>=X{Q%tOh8?{4=i%iTocpQx)gkYfe zD;BD!h1kUQHp2 zWut@xYw2xsA7wVUq(HQJiq|lq{U6w+9oqH_arEkjc17z09jj!bcpcwJMM3zqhKiD? zJ72w9d_t^QaZwhdNr^70fv&E+`m?Qe*;%pf4Zpzz?<5jG=r)Ifz!$vOpuC>R=fXUJ ztYpfd#Dqk;r`nqiGx5k44j}3yRtt6Crr1_S3scv?mbEND=3K0Axv?%;+CzLKP51X(gnFd$Qwn z%!3Q%*D&)3n(6OB&}gG|9cN>%B)3~YtV_dFfH=ZHNMiM1p!EF8nrw*e@%eZ+ifuNZ4YyJaR%d(@}7FE=qaeHHR zV|7**ZQv`SInChl_wog~arcB@zep&5j4=f6_s{YF_iB9T`K%hG7Wb6N&qK-hW~t?} z8>7vpiVm`nQ?}n42V3&q*4ztJ$atHd&=n26OBj%3YQ(iS_Rc}ahF45RU?x7-aip>ZUoPZ=2s1cQ@BpWdrfFt~2GX=GzPwtQ{!z-6TC zUP;PE%0`qtQ%+`dQ!BDz{*9U0aO3e@SB@ISsI=O5O?>OG#`gkVVhRIX%j-qnKhVS{ ztRY)j>8QwypgWVcRG=fZ0nVN|VMuxbWUuHz@J;|6a=YOeh#S-9S}kBCeIac*F`uH; z0n(LkD1kz`-MG~U#e@&VT9|{iBJPiUA>{8UM&jPB_`%kd6)(6J!bdbV6sg*Gt;@!i z9wm~+I0{4%g|4#RE2>n-t!rHwrtCj}FzFq_&xaTA`acZRz>Le!Jnw&{evq==u+Eb% zTx2lr`F;Bp$m^}xx3&*!x^Wdgbx>)yJ88@d27N%+z*aigc>6`mAYZ*lEAk0kwW zhhc5Kiem@9g=89jeRV?oFZed1v<~3s=;!5m@e0sNTV66bA5|u2cxlcB%FG_*Fo11) zccAt`q*j{v@>9dl1+UhqC(a`68w;rsR#&30?!A11)nCC~5V(sW(s-&}b@SkOP*5Lc zVlQf!W9KJ_WUN^B#F%b0Pahl%1rw`1pCI*<foVOSGTp8*7SWEuUi8F3> zWm>w}Jrbv_sejyrKGNq50d4`i9wfgy5UAA~ZJt+I>3+^SrlxiTJYA;WRQ=!L} z=_*I_`(-sImeTyS?`B(3^Mh}{vHt2+yt(-C%Z@8}@vz$$7?(Ok@)%Q$K=CAX;aA<+qx1KfaYmXlD;V&uKX9C#EJ()}L zTVkhLNSktr`7npxw9s;5_M--e28tusCekOUAHXK_a^g2=Y?zzOsxWavT%m z{OaeQB{(3>Mhv5vhN`;)w_IE!CQ2V(*wy#;JmIf;Or#B2d>todjo{^XV?$mwYt z)o}9w$ZE~N2&SKfp?Ns6!IRX`38E^F_* z8105Y`F%CL(}<5oy0-T))dyZq-d9__W3brE5{b8`7&9c4te;9MIeXiEMJ=#MR*jA| z?TywZs*hCp49XGjl}r?Cx2i;YI57m)hRo;t*$5A!359z9Edk&7zXNx_ACxJDHBzT# z0O^dT6(y@LXf5m6&4;sb>S5l)Xfg5Qkvoo=rD9-dW>U9$Xhx76j7>I;xj z`NSJLtK839(68KjWKHek-?uZj)r{|6eLwifaA>g1BPYk&oPViSR+>;2l=r+Zm}g;- z0F?B<4=#YZhHUk6n8sQ{PE2ggqN>ZK0_?2}yQ-t?48gC$5{MsE19d~K}$eP0$i{iVI8Z_CB0V~3;OJ(Ion^1JrJtmo$EBa;!2Tt zO$KG`>zMHxknSc~aK%6Vdz$k>vOJ4?w$E9LOw#=g-s?tDJ?O0uN-k$LQ-NN_ZUs*9{^F^m96c;Oa*_S}N#0Z5vmGOr*gjPJK#pG_ za_toVqc@bIA<+Xmk%NO>pXK`L#Y6=;izjtd0E$6Vet|;p%@^!q@)*XAZE(j+Ow~!o$64pyENx)VJiF!p{*(}V3OQKD*SB%yW&%K@wu4)dr z=y!d(RUYltz#E;PK&GRwB{^wpqacz27art!UKAdhOfSherSzzA@Q7--GVwhkGbj0K zm6JmEyK$pe$gA-x&Meqce3PjGX7FqEJXl8*HZ)tZ_SSgwvq*(A6PX~+pScl9&H zwBsNMgh^hTnS_9(!Prw7&G&g{?9+w;u{c*7b|$1v+;lP^&=8Jg=dm5Ws@MzW90W@! zMJvaXxAyIXefhMKEkZ@cV5MmHP)-1*Vx%jx^t|ZPf&(7+_n6f0M{2+S2J{be6a3dP z;qQ<1{S8HRq>R=i@~ul|Z@A46?oqT0-QfO7y{ilyu_3xa&uu&~*&-s^9X;9Z+Z;Tu zpyB~Wx}ya`{|d3!`L0ulcdqz+6+cq->d7Gt=LVl9iSRw6;83+uI2jLlSW0Ojf~^S$ z?BL=GB6`J5_o=RO^GgfM-XK%5COSPbE*SMKI<71{duwOHxD*ZduvB|K{sN1Gpx#?} ztI`YXcI8rA@#_|?SzccAoCHfB|_M!y=->u>lj3r%|QCYUP!D6+-k`n zzT`YF{Ih?-jY$KzwRJ8h-`tI^{JVN@KNRm;Vmfoj?`y}scf(W|-ZVkldVY)yScuf* zT8$t-9RlN}&Y*VXeWd+~IJHTRM;LC}A>!UVgWW1==I}>GlnOe*T#C$}+{vW-NFhp! zt+Gv_QJ}iI)l8utk(brzI_s;>O07`*KyLtdL%3A;R)`KCTi~;psdsl%C+`*9lHO{r zA;ZWJrMN4mJsrvjo{r84t`N?cH3o)AxfJI!6>)b8F1Z$yg*guCK0F#i+|1{nt(#l; zSYu8;ZRmXQ4ksUblbZIV(Z%O3)Z6%0NO&(=H`ih%sbq}8p}SIq5S$bxh_b$VU&MPL zQcWkksfeS;c&D#XBVkXct=ApT~sE($4)_w{%t;8_&;x< znfn#WjVz3)Z$>I?en#djJq_cyg9uK#N6x3lQP_Jg(jQtTQ#Tf{wpvlv%RgM9l~HAq z6T5HZzaJxS^Wyad5?6|#kInFQyk70us#l2|*4b9fgVE<^s7`);v2?o}OrwN87nR;Q zRs24l!kWiv@8DYEIb^hZdSPZ5{QU@Z+jV(9m@LJ(7ep#C6JHKV=nRN0u117ewbEU_ zsUb-#{YICb&B(O&D5eSRA2T1RXfO4Im+_5u? zpDFxLNS{%vJ#(JkMC18at}}Ao_mpfQ{wh+=q$(0zQ^GSa-}Gd}0f9q0zx^1@)2SY{ z)y;TB(wl;HFXEAS3x{^<%Pt7i9t{F_`>oCi=vH_5H_#H)mk8fP9bI>0-&F_R9!B}D zLAN~t&Y%w^0Ij2HYW?I!pXk@a`y&39>MW{?a;mDFmo7$I`g57S^n-Xk^3t;?Ss}QD ziet;ra=di>7nEOy-`0`KYEQ>Y#ZG(pOI7b@gfRQzI<79?;nK^Jed@#U-yV_t)q(V9 z9{isgeE*o=`xCxXM{`r@=?tQ|#i;*HqY9-?ubufAGq=i&&ATENqfMA*zmhs#pNif? z-a|*Kht*Wc-f%+8rrT^cyT@azQMa74_LE-&2hR%&R8-t?W(K&yRYa}i?%HtFTI!X4 zXtZpX>9cdkw=|uUkSCYZOz(E$Jl*)3A@7QNOcss zUzp8OXXehPGOy$N=Cc{1o}Bk{xFVZ2J>D*uo@u*wHSQYH5@_bEs zIrX*J;}2ZqovhBNrCgQdln64EKl3Rp;{>>ER<{^3O5D?5>3+6h*nRUvoE&J7iz6fC zIUL@>s3XR&9Wd}(Qr5jr$=q})R6w%SV`ghF{B+n3aP1BQX#S5`YOGb=A2 zk?&}!9LbUFfg|SdW1gJ0NMXar4vc!bxDs0}GwvweS^K7>?$cu#!}jocfW?qn1!pOk zpDcovE3oxJp67lLuUw@XseF$KH!Ngz{jPG|OWNmkr+DroF$)DH@HJ$N{uv<^(upEJ z$yeIX3dn)0;lYbF$+~I2bLb=o#b<32!=T`vz9BS63Nsr=3cH46<66xIf7gY&#=BAF z1=M`9kL{SX&1RGrR=qltiVNXaM`{(T@LjYQc4_aDC>_ltlSIHu^m%jE%LY6grmLtM z-{o6{a-XL34O($q-Ukms z%AaX8*WwTOYKSZpksWrU)|*I)3|4Z>b-s|tY$fnmglOnB-iOg=`7gkPtX{(5az?ku zzXpuTc%n+Gu@ydHb6ZiSu;=!|jaT|=7&awUoTzvr6jCLen8>n^(GT$U!?pmzb%RHr z%k8B;h#!7grnkfNH|ujNn!oIc7I1|J3qMf4lx;flp!!ZB z&uz=4txfgI*CuI#`nm6LykCCf6mR`WRu_bQ=aYlIY2ouJ%DD#dP@JmW>#0q-KsxeT zkB@}IGcOn0q*c+EWQ~4eqc_*=#>06tVrrUc;cG6DBJ2U=$7cUoEz+M z4L@wR2eOV0?AG5vxS#;6?(smRdW|=v&z}w=o${SbeJdr4wF3f z2Qy?p%q#o}NAkBn`E9dW?RVhvAIkpyiQi@l^Dw=rDykXPLaY2*ncdKagE;!jBas}L zbei=YmtYk>v(R@mx72}B0s4kaPm}x51QyL=HFJGnCxH)w3T-nhanZi~_7V&&G8M(G zF4eNhw7vt&`zTsa5-*>>a|(pFG{ei<;pd_AIHg&Zw*=3MJ7qy0Ps0RH>*tMJ;zF0)Tt;F-MLm03>?P}3u^7(8`$+tbx9>!H3pWFR{K`y9{m43- zclM*HD0)n_a%G%|RV}k`UA+hCw0;tch;tW$;G6bML@VB>R)=Y_)+;J!yw^{f;A7y3 zUhuN4BB1O_;xL*Ep{dwE=n#SJfp1ni{gU-W3N@6%NX^?UeM$Y7}UxLKKmZ%gCu z<+icv2Yjpzl{OYhNh(&C(AVvjRw{Vcy@JwM{XaHqJ>avaMo4}-cKgJ@u^874oG9CZ zGn8vJY061YWHa@1ht$iE76a}Q2vUDSeAt0H?^*g2qmiQEH@E8W^Sak`|q|R~%L3iN}~H&OE(L6)31X^d(k4D=l$di)c{+B>o$wphrCuM|19D zdc;J8IJ9pAwaS3bb0YLW+h&25gT7+u(jkVm+Y>P)x}#*sj+9_DUWG$Yh-snd@patT z%WmDKw_>;71%4TTd?*mT#gNNr$l8I-xHzUS&sQ`#&q?U*c#PfbLvG5Z~?mz^(08@u&> z)`lH_qjOtifq~^KBnh{5xxG??!%Oejg;(DA%OmFK-Te$@d$v2g^hcd#?ph}fZ%5wG zhYP{aMyI4aUBX9*Lz;S=)xoNQqN8~-!m8Dkz3FeXkHh+y;?K7)p4x~>qg5r#9#pK| z&GK|GN-GmD!x$OeB0T3wkcfH98J?8pIwQP?Q!(~m_k_>S=#+=4JoZexO}9kbH76gKa-vMqb}+&27?G)1Tvw!a3P8$1 z_z`l3s$A>)yvw{o+b%moGI18ebiE}Bv;yGm;fQm1V-BbLBfg9DM|HiHbg%VoU`h_m zlJT^Bc_&1-SWPU&5JOu^BIBdE;Jjh{=@7c!$T>IN^AmE%T*`OF7sUQ{t>I62oB!!g ze%qY&L*1(8aLct&H`OWv!`*|brSUlDnc{&aPiA$*$CShzt3{Jj%3+iqoRsiu6vIpE zb*x7d?kubA;|`AIsA`zrTbFpc0hM05`wv77*0lj33!Asfa%Nrs$mj^cx$UGaX;Y!a zh0t`89E`jayY5pi*mDYSL!xrx_t{l)X6Z?ou60g6@+RP^O{)K zm_F8~1;cD%!|oi8x7)*7q;S#~azmnzy>i;whPO~MD_*f!k0$<(=B78j0ob(qMWbD`h58<}>d=D~ zZ>MX^jVgtS=(qG+9Gi>7%#X4F_0IjvYfmtEWqX8e?tE2rLvH^=chah3pJc??JbocBPHr1dGC#I8pg(9Z zD?{ZbVs6Vwg%nr2!=MA`MDKs9Q!#b*TI(5;p5P#(Y`U9;pLgQ1B%z2%4|Xd zT|u_5zvg@BkYwG`Yq16@r}tR8Ud*~>)DQKW2)m5F>!056XrsJq!$-*zo(eP!bCsm} z38);v?lIznVjXrS=A|fNLi3{+A*6aGJePt+!+3`M?*t3MLHX*mD$(FHWz-NpP9O%9 zG$$Ufo8#6~Sm%qW~36qZb)nrc0O8o0)Yw2Cw-oq>zsxfnxl)j*ngtNfT=<~-A&PV zFq^QH2k~vsX$Zvze^D=n3yVTA*vV}cS*ob?jIg9Y2F9?D#952bfVd+&)!MHF-4B?~ z>t5l|a+T@!Q`OGsBX5S)3hA>)R{|NZ5K+gMAty#G9L`G$neMp>qXj%?tDt7#QTYy0 z^(wR*76QkMkY#2KeC=84^_;qc5cX|HS)0C7f*TeiVqh`#pobG8 ztSx+KCiN=OIY;y^SD3#K}ulyYair(a@nes4GM(iWwxUXMM5txfPSOcEReD(yInUTJwe= z(VU_A8z?pbplo`6h_9IjF)DkfQStu{4@8X&m1rXi%1SPK39^p zI6}3PwlB_S{>ps2UV>@ZxP#O_F0>rJSV@1B zFWHQg?t*vceJL+TGN)6XPD#_^E8=?ya(o=6e;@;AX?N6dIKbo!T<1Q0j&nH5RL{-S z@s*kSEiD>y@bT`3S-Ids0GORuse6s8QC{UpWyV9b9@`eM!yPmXG5JYG3lq zBgL2?FKoa{ddfFnJ8OG~I@$Zea73)Dr;Sw+pGTbdK~IbUw(P8R4vQ6f{fr^LY3e;%JU zjGg{nK*@hc#HN|-)8DN&hKXZUcyN2Qx@3nE_VJDtADmiEG)3rGGgunF;2V;6VuCQ4 zh0@(Zdv)R-xt|reiNResPRDK3o@Ut^3A7vM9DB0UUuZKM>|pOHJQX1lTF1@BjOSZ4 z+07+Av3W7fU)GTwVDATNoVm$+Y3g9v+ja^`yqDZrfIpbsB?)}mXO}m z5F$_Wx!k;<)Q>gyUFcDxD3;EdR%k7-tuXNC%Um)LDm-Hy@M+(*s~b8SX6)jQJ$-A` z451}&aQ2uh#hi3=+#SPlRo8!>RLXCljz4zW3YqU_4s~i%)^v@2vVecFJp*r@EFRn; z<>JaSwSilkpUp8d@uDJJ9NX85!RNkkuW`ZzVtO=?bBG|lz8lo{L#(d8FmSQ8s9Q_0 zyMwhKw_lyecWD-VT`sLg?^oEBC;?5wya0;eG5~I`$CBzBXy7r>eLsYcfG)lQnx9v~ z(8TiX(gSoSbh{rAZPAiAqUe3LZyl8VHIW=8(XF$JNqB`pOD3>an#HSymTj(g2YqY< zx;s6r=Q7$%maV^7zl3>~!%x!HUu$>r83EVMkbYuQ8Uc!E(ry54X(1dT;r#G{(sg_G zUCrEY+SV|b0&gQE=L@PRTP!i z2KVtyDEOZrt84fu~8A@`T$dDz0$FrhqurDzE&>7 zA3COYap#09Mfxf29#b(!bj-l1^N-6isfa<$n`WpK)yX8de878_tX`?k>s<-_BN(R8 z75mYQG|Wi;1`<&)^SN%JX?k;~l0RRs(w2AQ9rIn}s1lq=e{4Id`))`FEpwoDnaNod zcVC@ZpQTwq-RMzKb(Ib!o2^I2ljBxQy|3w|uio9ZK&(}dqffrF9~a1<-UMR6qW&qN{ziJRwC3ky!s!|f zl-D#B>1-HoOofuBJEr;p04R#$M?wt_r*wBWu07)j)X|{Vxs^gm4gx&^@lc$8Z4OgO z_q2$N*Ztn=5Cv1Y$(6*l6NOIH`ZG0_KZY*;E!gD8kAnIQ;^@cj@LvzYe(~u~)9$+1 zuGNqZj;ynl>m$;qbw7;uPdjaEO)r0x44}beW?Rgh?M9K^rg=Qw%fH81De@3kJ8ndS zKPkGj_!Lj(E2tNF=t!XrL8VT_{*`4MVJdsC~4nk_HI@x z1YoD%r*R?LdwpDA+VY;_kd%H+!de|(oF!+sk@+TV>v7)_`Y|!ui{y2mrV5p=A90Gc zsI!fQxNEPo>U8nkND6)WW%PU(YQ#|0%p|bbP~P680z*bmx!FHiyuE$4n}@Mxhm?jZ zXr{E73+8}(IS@7Imy&fC^?Ni&KQ1Tz>s0@k1^ub0ioe7Limd&c4pRNbml05tKG*S? zW$=9R*Mw4&HE>Q+Y-}zI63r;5Dbi|OP#GwtM-|1m8%hn~Gb{l1C+-Jn=A=0G)N zavMpD_z<=0gB9M6#64LBS4CZ7hu%CsNZowWS$`~3Ct$_`-lcWwi_1<4xiR;BnfNN+ zkNMBKRgU9%yJV4NnHL(Njww&=cBna8fd>GTr}uuMKwSdK;Y&4 zoj4%czvty;x7wK&a!-8llS#!8n`AJutnrCs^urJ*9lZe+eM60C?K7Y6m+B1O>r?)0 zkNc7kwe}_I%MR{>r1yV`JpcW9{fBk8j*4`1Wfv>8cROMO5 zXmddM*ynXPxZd<*dNURDjEwe&7e*f*XH;4(cM+GpO~Rh+)%d)907`T&C8s|sMChPtkO13|idfiJM@{n)NjqBq_d;MO57V~Oab zqp;c@rqODa{bVRVWO~r&?dW^osfGORN+-{FTve+o`eAM7>E}K%mq~pZ6%E7DUi~g+ z%Z?faQ>l01(jSa%4XIT^(_*CI;IQ)H-n41F*&v?r_I9}a3{Ogv-fps{E5dAO+FCW< z&gb#o$UCD>EyerhmJy#-#5L2@?%j`Jf2W zF}5EqVgKV}+`sB?LjAOC^zWNWC`CJJUgP3R_F0ya3$N42hv$+NQ>#uK&$u&I(897r z!WF*cGZX;ukHsA&bkC~;3DmbEvIhnjpkZx<{g5+V2PS4pVp4iLnSp=&sV*09&nBz9%Z#(&2D;)j<6%6(AZj#XvJv^N=rW z3GBP)yYe;hlY7T|o`V$o=xn1HSfP{$q&%fLMoiLoZLK?u$XNagJ&YWSn?M{zUTG*i z)PaUW35(+4$fPTUE;iuhABeU1bvA!;)CBzvcg4>cdgyO@7FUW^G6~;6stHi7HKV#(uRPRB8<2*jAq>p}yO}pM5iplwl-t1$pe^4~ zxYYxgi@moOfLwStf;~#~!aEcKr`6y3qsU^O~ZPOH!rJ zn~_TUFH#kE7Y@?x2is2F_Y!p^J6`6|PZOauPiXGXZGsf53)BwHLdj7pZ)8}ZQ@Mx0 zs92RUpMecPW#>N${{Arvfxp_#|8Ng}TGH&-{qSFz3y?jL?LWrlZMb)uBv^#Zjp1P6 z_=^VXl^`SeEmNgE!tnQ%7d+4-LQ(j)$t2Ki6Nhf%*YP9t_e1FKdOTdZzlH0bqo@L` zgysWAzlRk{`xIq>(=$-UdtX6;Rb}JEOC1fvCwTrA4f4+FZK?!c5Wr&qNXQcX zefM{O2seC+hUf9Iw>tyH+I~mIo$z_|hYe|As1JCfItP43H8Qr*$+67fI$^weRF`2n&qe#v2LhJ5-?hAOcRPmTiQ)-BtY#a zga)+t#$;&7gHi)nUpGuS4n#Jp%7r$WEv5yXFx3g;iPX9Ei!h_~KVq8p|JIuSM=~=1 zuO>MEWcuu8>9a literal 0 HcmV?d00001 diff --git a/docs/images/Configure_Three_Way_Replication.png b/docs/images/Configure_Three_Way_Replication.png new file mode 100755 index 0000000000000000000000000000000000000000..3f741a916324f0e931b0fb1a61cb43c5075947f7 GIT binary patch literal 67305 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

oE%oW`I=-&M3^vCY;)+1t!n^3*0SbZ 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