{"id":19345931,"url":"https://github.com/tolitius/chazel","last_synced_at":"2025-08-22T13:04:47.529Z","repository":{"id":29408394,"uuid":"32943835","full_name":"tolitius/chazel","owner":"tolitius","description":"Hazelcast bells and whistles under the Clojure belt","archived":false,"fork":false,"pushed_at":"2020-07-26T15:56:06.000Z","size":157,"stargazers_count":72,"open_issues_count":3,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-21T03:26:50.115Z","etag":null,"topics":["clojure","hazelcast"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tolitius.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-26T18:05:42.000Z","updated_at":"2024-09-23T05:53:30.000Z","dependencies_parsed_at":"2022-09-07T14:52:20.916Z","dependency_job_id":null,"html_url":"https://github.com/tolitius/chazel","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/tolitius/chazel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Fchazel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Fchazel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Fchazel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Fchazel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tolitius","download_url":"https://codeload.github.com/tolitius/chazel/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tolitius%2Fchazel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271643468,"owners_count":24795440,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-22T02:00:08.480Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","hazelcast"],"created_at":"2024-11-10T04:08:23.046Z","updated_at":"2025-08-22T13:04:47.389Z","avatar_url":"https://github.com/tolitius.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# chazel\n\nHazelcast bells and whistles under the Clojure belt\n\n[![\u003c! release](https://img.shields.io/badge/dynamic/json.svg?label=release\u0026url=https%3A%2F%2Fclojars.org%2Ftolitius%2Fchazel%2Flatest-version.json\u0026query=version\u0026colorB=blue)](https://github.com/tolitius/chazel/releases)\n[![\u003c! clojars\u003e](https://img.shields.io/clojars/v/tolitius/chazel.svg)](https://clojars.org/tolitius/chazel)\n\n\u003e ###### _(!) for pre Hazelcast 4.0: `[chazel \"0.1.20\"]`_\n\n- [Creating a Cluster](#creating-a-cluster)\n- [Working with Data Structures](#working-with-data-structures)\n  - [Good Old Java API](#good-old-java-api)\n- [Connecting as a Client](#connecting-as-a-client)\n- [Distributed SQL Queries](#distributed-sql-queries)\n  - [Jedi Order](#jedi-order)\n  - [Jedi SQL](#jedi-sql)\n  - [Query Results Format](#query-results-format)\n  - [Pagination, ORDER BY, LIMIT](#pagination-order-by-limit)\n    - [Paging Jedis](#paging-jedis)\n    - [Jedi Order (By)](#jedi-order-by)\n- [Continuous Query Cache](#continuous-query-cache)\n  - [Vim Jedis](#vim-jedis)\n  - [Is Continuous](#is-continuous)\n  - [Is Fast](#is-fast)\n- [Near Cache](#near-cache)\n  - [Client Near Cache](#client-near-cache)\n  - [Server Near Cache](#server-near-cache)\n- [Distributed Tasks](#distributed-tasks)\n  - [Sending Runnables](#sending-runnables)\n  - [Sending Callables](#sending-callables)\n  - [Task Knobs](#task-knobs)\n    - [Send to All](#send-to-all)\n    - [Instance](#instance)\n    - [Executor Service](#executor-service)\n    - [All Together](#all-together)\n- [Distributed Reliable Topic](#distributed-reliable-topic)\n  - [Replaying Events](#replaying-events)\n- [Map Event Listeners](#map-event-listeners)\n- [Serialization](#serialization)\n- [Stats](#stats)\n- [License](#license)\n\n## Creating a Cluster\n\n```clojure\nuser=\u003e (require '[chazel.core :refer :all])\n```\n\nlet's start a 3 node cluster\n\n```clojure\nuser=\u003e (cluster-of 3)\n\nJul 25, 2020 9:47:57 PM com.hazelcast.internal.cluster.ClusterService\nINFO: [192.168.0.107]:5702 [dev] [4.0.2]\n\nMembers {size:3, ver:3} [\n\tMember [192.168.0.107]:5701 - b48fb15a-ad9d-4ca7-8d8c-461920ee71d6\n\tMember [192.168.0.107]:5702 - f050f3b1-71ea-4814-884e-52d150f3781e this\n\tMember [192.168.0.107]:5703 - 93f7dad3-4f34-400b-9cf5-58a11c95a59c\n]\n\n(\"HazelcastInstance{name='confident_mahavira', node=[192.168.0.107]:5701}\"]\n \"HazelcastInstance{name='unruffled_mahavira', node=[192.168.0.107]:5702}\"]\n \"HazelcastInstance{name='lucid_mahavira', node=[192.168.0.107]:5703}\"])\n```\n\n## Working with Data Structures\n\ncreate a map (or multimap, or queue, etc):\n\n```clojure\nuser=\u003e (def appl (hz-map :appl))\n#'user/appl\n\nuser=\u003e (type appl)\ncom.hazelcast.map.impl.proxy.MapProxyImpl\n```\n\nuse the map:\n\n```clojure\nuser=\u003e (put! appl :apple 42)\n\nuser=\u003e appl\n{:apple 42}\n```\n\nsome other cool things:\n\n```clojure\nuser=\u003e (def goog (hz-map :goog))\n#'user/goog\n\nuser=\u003e (put! goog :goog 42)\n\nuser=\u003e (find-all-maps)\n({:appl 42}\n {:goog 42})\n```\n\n### Good Old Java API\n\nSince Hazelcast collection API implement Java collection API, Hazelcast _distributed_ datastructures can be navigated and manipulated in the same way as Java local collections:\n\n```clojure\n=\u003e (def alpha (hz-map :alpha))\n#'chazel/alpha\n\n=\u003e (put-all! alpha {6 :f 1 :a  3 :c 4 :d 2 :b 5 :e})\n\n=\u003e alpha\n{3 :c, 1 :a, 2 :b, 4 :d, 5 :e, 6 :f}\n```\n\nand now:\n\n```clojure\n=\u003e (into (sorted-map) alpha)\n{1 :a, 2 :b, 3 :c, 4 :d, 5 :e, 6 :f}\n```\n\nall works as it would with any other `java.util.Map`, only in this case the map is distributed and lives across cluster nodes:\n\n```clojure\n=\u003e (type alpha)\ncom.hazelcast.map.impl.proxy.MapProxyImpl\n```\n\nOther Hazelcast data structures, such as lists for example, could be manipulated with the \"same old\" Java/Clojure API:\n\n```clojure\n=\u003e (def anum (hz-list :alpha-num))\n#'chazel/anum\n\n=\u003e (add-all! anum [0 :a 1 :b 2 :c 3 :d 4 :e 5 :f 6 7 8 9])\n\n=\u003e (group-by int? anum)\n\n{true [0 1 2 3 4 5 6 7 8 9],\n false [:a :b :c :d :e :f]}\n```\n\n```clojure\n=\u003e (type anum)\ncom.hazelcast.collection.impl.list.ListProxyImpl\n```\n\n## Connecting as a Client\n\n```clojure\nuser=\u003e (def c (client-instance {:cluster-name \"dev\"\n                                :hosts [\"127.0.0.1\"]}))\n\nINFO  chazel.core - connecting to:  {:cluster-name dev, :hosts [127.0.0.1]}\n\nuser=\u003e c\n\"com.hazelcast.client.impl.clientside.HazelcastClientInstanceImpl@4786a6b6\"\n```\n\n## Distributed SQL Queries\n\nHazelcast has a concept of [Distributed Query](http://docs.hazelcast.org/docs/3.5/manual/html/distributedquery.html) with quite rich [SQL syntax supported](http://docs.hazelcast.org/docs/3.5/manual/html/querysql.html).\n\nchazel embraces it into a single function `select`. Let's look at the example that is taught at Jedi Order.\n\n### Jedi Order\n\nSince Hazelcast internally works with Java objects, it relies on getter/setter accessors for its full SQL power. This is not that bad as it might seem at the first glance. Think Google Protobufs, or many other Java serialization protocols, the all produce objects with getters and setters.\n\nLet's call for the Jedi Masters:\n\n```clojure\n[chazel]$ boot dev             ;; this will load Jedi type\nCompiling 1/1 chazel.jedis...\nnREPL server started..\n\nchazel=\u003e (require '[chazel.core :refer :all])\nchazel=\u003e (import '[chazel.jedis Jedi])\n```\n```clojure\nchazel=\u003e (def masters {1 (Jedi. \"Yoda\" \"vim\")\n                       2 (Jedi. \"Mace Windu\" \"emacs\")\n                       3 (Jedi. \"Qui-Gon Jinn\" \"cursive\")\n                       4 (Jedi. \"Obi-Wan Kenobi\" \"vim\")\n                       5 (Jedi. \"Luke Skywalker\" \"vim\")\n                       6 (Jedi. \"Mara Jade Skywalker\" \"emacs\")\n                       7 (Jedi. \"Leia Organa Solo\" \"emacs\")\n                       8 (Jedi. \"Jaina Solo Fel\" \"atom\")})\n```\n\n[Jedi](dev/chazel/jedis.clj#L5) is an example type that has `name` and `editor` fields.\n\nYou guessed it right, we are going to rely on SQL query powers to finally find out which editors Jedis Masters use!\n\n### Jedi SQL\n\nNow as we called upon the masters, let's put them into a Hazelcast map. We can use a `put-all!` for that:\n\n```clojure\nchazel=\u003e (def jedis (hz-map \"jedis\"))\n#'chazel/jedis\n\nchazel=\u003e (put-all! jedis masters)\n```\n\nLet's now run some _distributed_ SQL on the new Jedi Master database:\n\n```clojure\nchazel=\u003e (select jedis \"editor = vim\")\n\n#{#\u003cJedi {:name Obi-Wan Kenobi :editor vim}\u003e\n  #\u003cJedi {:name Yoda :editor vim}\u003e\n  #\u003cJedi {:name Luke Skywalker :editor vim}\u003e}\n```\n\n```clojure\nchazel=\u003e (select jedis \"name like %Sky%\")\n\n#{#\u003cJedi {:name Luke Skywalker :editor vim}\u003e\n  #\u003cJedi {:name Mara Jade Skywalker :editor emacs}\u003e}\n```\n\n```clojure\nchazel=\u003e (select jedis \"name like %Sky% and editor != emacs\")\n\n#{#\u003cJedi {:name Luke Skywalker :editor vim}\u003e}\n```\n\nniice!\n\nIn case a database / map is large, we can add [field indices](https://docs.hazelcast.org/docs/4.0.2/manual/html-single/#indexing-queries)\n\n```clojure\nchazel=\u003e (add-index jedis \"editor\")\n```\n\nnow this query will run _waaay_ faster:\n\n```clojure\nchazel=\u003e (select jedis \"editor = vim\")\n\n#{#\u003cJedi {:name Obi-Wan Kenobi :editor vim}\u003e\n  #\u003cJedi {:name Yoda :editor vim}\u003e\n  #\u003cJedi {:name Luke Skywalker :editor vim}\u003e}\n```\n\nfor larger datasets.\n\n#### Query Results Format\n\nBy default a distributed query will return a set:\n\n```clojure\nchazel=\u003e (type (select jedis \"editor = vim\"))\nclojure.lang.PersistentHashSet\n```\n\nIn case you need an actual submap: i.e. all the matching map entries (k,v pairs), just ask:\n\n```clojure\nchazel=\u003e (select jedis \"editor = vim\" :as :map)\n\n{1 #object[chazel.jedis.Jedi 0x44bb1c0a \"{:name Yoda :editor vim}\"],\n 4 #object[chazel.jedis.Jedi 0x4ad0c3c5 \"{:name Obi-Wan Kenobi :editor vim}\"],\n 5 #object[chazel.jedis.Jedi 0x2725fbd0 \"{:name Luke Skywalker :editor vim}\"]}\n```\n\n```clojure\nchazel=\u003e (type (select jedis \"editor = vim\" :as :map))\nclojure.lang.PersistentArrayMap\n```\n\nFor a better interop, you can also ask for a Hazelcast \"native\" type:\n\n```clojure\nchazel=\u003e (select jedis \"editor = vim\" :as :native)\n\n#{#object[java.util.AbstractMap$SimpleImmutableEntry 0x69cfa867 \"1={:name Yoda :editor vim}\"]\n  #object[java.util.AbstractMap$SimpleImmutableEntry 0x3b0a56f9 \"4={:name Obi-Wan Kenobi :editor vim}\"]\n  #object[java.util.AbstractMap$SimpleImmutableEntry 0x3b498787 \"5={:name Luke Skywalker :editor vim}\"]}\n```\n\n```clojure\nchazel=\u003e (type (select jedis \"editor = vim\" :as :native))\ncom.hazelcast.map.impl.query.QueryResultCollection\n```\n\nIn case a wrong / unknown format is asked for, chazel will tell you so:\n\n```clojure\nchazel=\u003e (select jedis \"editor = vim\" :as :foo)\n\nERROR: can't return a result of a distributed query as \":foo\" (an unknown format you provided). query: \"editor = vim\", running on: \"jedis\"\n```\n\n### Pagination, ORDER BY, LIMIT\n\nSQL would not be too useful if we could not do things like \"I only need first 100 results out of millions you have\" or \"sort the results by the revenue\". In more SQL like speak, these two would be: `LIMIT 100` and `ORDER BY \"revenue\"`.\n\nHazelcast supports both through [Paging Predicates](https://docs.hazelcast.org/docs/4.0.2/manual/html-single/#filtering-with-paging-predicates):\n\n\u003e _Hazelcast provides paging for defined predicates. With its PagingPredicate class, you can get a collection of keys, values, or entries page by page by filtering them with predicates and giving the size of the pages. Also, you can sort the entries by specifying comparators._\n\nThink about it as `LIMIT` and `ORDER BY` with pagination built in: i.e. once you get a resultset back you can navigate it by pages. Pretty neat :)\n\nWith chazel it's just a couple of optional keys to the `select` function.\n\n#### Paging Jedis\n\nUsing [Jedis Masters](#jedi-order) example from above:\n\n```clojure\nchazel=\u003e jedis\n\n{6 #object[chazel.jedis.Jedi 0x7eb421d4 \"{:name Mara Jade Skywalker :editor emacs}\"],\n 1 #object[chazel.jedis.Jedi 0x39208ed7 \"{:name Yoda :editor vim}\"],\n 4 #object[chazel.jedis.Jedi 0x4f001c4f \"{:name Obi-Wan Kenobi :editor vim}\"],\n 5 #object[chazel.jedis.Jedi 0x417eede1 \"{:name Luke Skywalker :editor vim}\"],\n 2 #object[chazel.jedis.Jedi 0x1e9bde9b \"{:name Mace Windu :editor emacs}\"],\n 8 #object[chazel.jedis.Jedi 0x2370bda9 \"{:name Jaina Solo Fel :editor atom}\"],\n 3 #object[chazel.jedis.Jedi 0x6cdd2fec \"{:name Qui-Gon Jinn :editor cursive}\"],\n 7 #object[chazel.jedis.Jedi 0x5a7ac673 \"{:name Leia Organa Solo :editor emacs}\"]}\n```\n\nLet's bring them ALL (i.e. `*`) back to the client side in pages of `3`:\n\n```clojure\nchazel=\u003e (select jedis \"*\" :page-size 3)\n{:pages #object[chazel.Pages 0x58406675 \"chazel.Pages@58406675\"],\n :results\n #{#object[chazel.jedis.Jedi 0x170a94e7 \"{:name Jaina Solo Fel :editor atom}\"]\n   #object[chazel.jedis.Jedi 0x2b4d73f4 \"{:name Leia Organa Solo :editor emacs}\"]\n   #object[chazel.jedis.Jedi 0x6f1e19da \"{:name Mara Jade Skywalker :editor emacs}\"]}}\n```\n\nnotice the `chazel.Pages` under the `:pages` key that is returned, let's use it to get the next page, and then the next page, and then the next:\n\n```clojure\nchazel=\u003e (def paging-jedis (select jedis \"*\" :page-size 3))\n#'chazel/paging-jedis\n\nchazel=\u003e (-\u003e paging-jedis :pages next-page)\n#{#object[chazel.jedis.Jedi 0x7122e00f \"{:name Obi-Wan Kenobi :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x599d002f \"{:name Qui-Gon Jinn :editor cursive}\"]\n  #object[chazel.jedis.Jedi 0x5c4e9eda \"{:name Luke Skywalker :editor vim}\"]}\n\nchazel=\u003e (-\u003e paging-jedis :pages next-page)\n#{#object[chazel.jedis.Jedi 0x7eabb220 \"{:name Yoda :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x422d73b3 \"{:name Mace Windu :editor emacs}\"]}\n\nchazel=\u003e (-\u003e paging-jedis :pages next-page)\n#{}\n```\n\nniice!\n\nOf course we can also _filter page results_ with Hazelcast SQL (i.e. `\"editor = vim\"`):\n\n```clojure\nchazel=\u003e (select jedis \"editor = vim\" :page-size 2)\n{:pages #object[chazel.Pages 0x140ff895 \"chazel.Pages@140ff895\"],\n :results\n #{#object[chazel.jedis.Jedi 0x77b276a3 \"{:name Luke Skywalker :editor vim}\"]\n   #object[chazel.jedis.Jedi 0x562345a4 \"{:name Obi-Wan Kenobi :editor vim}\"]}}\n```\n\nYoda here did not make to the first page, but it is comfortably watching Luke and Obi-Wan from the second / last page with Jedis who use `vim`:\n\n```clojure\nchazel=\u003e (-\u003e (select jedis \"editor = vim\" :page-size 2) :pages next-page)\n#{#object[chazel.jedis.Jedi 0x59e58d76 \"{:name Yoda :editor vim}\"]}\n```\n\n#### Jedi Order (By)\n\nA simple [Java Comparator](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html) can be used to sort paginated results. While you can create it with a [comparator](https://clojuredocs.org/clojure.core/comparator) functoin, in most cases (as it works 99% in Clojure) a simple function will do.\n\nFirst, since in this example Jedis are Java Beans and the Hazelcast SQL resultset is a collection of [SimpleImmutableEntry](https://docs.oracle.com/javase/8/docs/api/java/util/AbstractMap.SimpleImmutableEntry.html)s let's create an `editor` field accessor:\n\n```clojure\n(defn jedit [m]\n  (let [jedi (.getValue m)]\n    (.getEditor jedi)))\n```\n\nwhich just wraps a couple of Java calls to get the value of the map entry and get the editor form the Jedi.\n\nNow let's create a \"comparator\" function:\n\n```clojure\n(defn by-editor [a b]\n  (compare (jedit a) (jedit b)))\n```\n\nwhich compares Jedis by the editor they use.\n\nLet's get those pages sorted with this comparator providing it to a `:order-by` optional param of `select`:\n\n```clojure\nchazel=\u003e (select jedis \"*\" :page-size 4 :order-by by-editor)\n{:pages #object[chazel.Pages 0x544d44f3 \"chazel.Pages@544d44f3\"],\n :results\n #{#object[chazel.jedis.Jedi 0x57367fa1 \"{:name Qui-Gon Jinn :editor cursive}\"]\n   #object[chazel.jedis.Jedi 0x1f14b62c \"{:name Mara Jade Skywalker :editor emacs}\"]\n   #object[chazel.jedis.Jedi 0x3b6118af \"{:name Mace Windu :editor emacs}\"]\n   #object[chazel.jedis.Jedi 0x57999413 \"{:name Jaina Solo Fel :editor atom}\"]}}\n```\n\nHm.. did not seem to work.\n\nAh, remember from [Query Results Format](#query-results-format), the default resultset is a `set`, hence the order is lost. Let's try to change a format to, say, a `:map`:\n\n```clojure\nchazel=\u003e (select jedis \"*\" :page-size 4 :order-by by-editor :as :map)\n{:pages #object[chazel.Pages 0x4e42e6e2 \"chazel.Pages@4e42e6e2\"],\n :results\n {8 #object[chazel.jedis.Jedi 0x2cc64579 \"{:name Jaina Solo Fel :editor atom}\"],\n  3 #object[chazel.jedis.Jedi 0x27400d5f \"{:name Qui-Gon Jinn :editor cursive}\"],\n  2 #object[chazel.jedis.Jedi 0x6908aeee \"{:name Mace Windu :editor emacs}\"],\n  6 #object[chazel.jedis.Jedi 0x56899da8 \"{:name Mara Jade Skywalker :editor emacs}\"]}}\n```\n\nnow it's sorted, so as the page right after it:\n\n```clojure\nchazel=\u003e (def pages (-\u003e (select jedis \"*\" :page-size 4 :order-by by-editor :as :map) :pages))\n#'chazel/pages\n\nchazel=\u003e (next-page pages)\n{7 #object[chazel.jedis.Jedi 0x6aa98c05 \"{:name Leia Organa Solo :editor emacs}\"],\n 1 #object[chazel.jedis.Jedi 0x5d4a1841 \"{:name Yoda :editor vim}\"],\n 4 #object[chazel.jedis.Jedi 0x63cd1e72 \"{:name Obi-Wan Kenobi :editor vim}\"],\n 5 #object[chazel.jedis.Jedi 0x2838423b \"{:name Luke Skywalker :editor vim}\"]}\n```\n\nLuke Skywalker comes last in this chapter, but no worries, this is just the beginning...\n\n## Continuous Query Cache\n\nA continuous query cache is used to cache the result of a continuous query. After the construction of a continuous query cache, all changes on the underlying IMap are immediately reflected to this cache as a stream of events. Therefore, this cache will be an always up-to-date view of the IMap. You can create a continuous query cache either on the client or member. (_[more](https://docs.hazelcast.org/docs/4.0.2/manual/html-single/#continuous-query-cache) from Hazelcast docs_)\n\n### Vim Jedis\n\nWe'll continue working with Jedi masters from [Jedi Order](#jedi-order):\n\n```clojure\n=\u003e (select jedis \"*\")\n\n#{#object[chazel.jedis.Jedi 0x1f987361 \"{:name Yoda :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x5f645ec6 \"{:name Mara Jade Skywalker :editor emacs}\"]\n  #object[chazel.jedis.Jedi 0xc9654d \"{:name Mace Windu :editor emacs}\"]\n  #object[chazel.jedis.Jedi 0x144ca20f \"{:name Obi-Wan Kenobi :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x49279cf0 \"{:name Jaina Solo Fel :editor atom}\"]\n  #object[chazel.jedis.Jedi 0x6e35e872 \"{:name Leia Organa Solo :editor emacs}\"]\n  #object[chazel.jedis.Jedi 0x63b4f296 \"{:name Luke Skywalker :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x1b0037fe \"{:name Qui-Gon Jinn :editor cursive}\"]}\n```\n\nLet's say we need to cache masters who use Vim editor. We also need this cache to be continuously updating whenever records are added or removed to/from the source `jedis` map. In order to do that all we need to do is to create a [QueryCache](http://docs.hazelcast.org/docs/3.8/javadoc/com/hazelcast/map/QueryCache.html).\n\nIn order to create such a QueryCache, we'll use a `query-cache` function that take these arguments:\n\n* source map: which maps to create this cache for\n* cache name: a internal name of this cache\n* predicate: to filter the exiting source map entries\n* include value?: a boolean flag =\u003e \"true\" (default) if this QueryCache is allowed to cache _values_ of entries, otherwise \"false\"\n* listener: a [MapListener](http://docs.hazelcast.org/docs/3.8/javadoc/com/hazelcast/map/listener/MapListener.html) which will be used to listen this QueryCache\n\nAt a minimum `query-cache` would need a \"source map\", \"cache name\" and \"predicate\":\n\n```clojure\n=\u003e (def vim (query-cache jedis \"vim-cache\" \"editor = vim\"))\n```\n\nhere `jedis` is a source map, `\"vim-cache\"` is the cache name and `\"editor = vim\"` is a predicate.\n\nLet's look at vim's type:\n\n```clojure\n=\u003e (type vim)\ncom.hazelcast.map.impl.querycache.subscriber.DefaultQueryCache\n```\n\nThis query cache is as \"`select`able\" as any other map:\n\n```clojure\n=\u003e (select vim \"*\")\n#{#object[chazel.jedis.Jedi 0x10fcece9 \"{:name Luke Skywalker :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x5fe3f833 \"{:name Yoda :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x2267043e \"{:name Obi-Wan Kenobi :editor vim}\"]}\n```\n\n```clojure\n=\u003e (select vim \"name like %Sky%\")\n#{#object[chazel.jedis.Jedi 0x44a03065 \"{:name Luke Skywalker :editor vim}\"]}\n```\n\nOptionally `query-cache` function also takes `include-value?` and a `listener` which makes it 4 possible combinations:\n\n* [source-map cache-name]\n* [source-map cache-name pred]\n* [source-map cache-name pred include-value?]\n* [source-map cache-name pred listener include-value?]\n\nIn case only a `source-map` and a `cache-name` are given, `query-cache` will look this cache up by name and will return `nil` in case this cache does not exist, otherwise it will return a previously created cache that was created with this name.\n\n```clojure\n=\u003e (query-cache jedis \"vim-cache\")\n#object[com.hazelcast.map.impl.querycache.subscriber.DefaultQueryCache 0x5a6895e5 \"DefaultQueryCache{mapName='jedis', cacheId='fd40614a-20e3-4c97-bb9c-2ab6a82bf638', cacheName='vim-cache'}\"]\n\n=\u003e (query-cache jedis \"vc\")        ;; this cache does not exist\nnil\n```\n\n### Is Continuous\n\nWhenever underlying data in the source map changes query cache will always be upto date if these changes affect the predicate of course:\n\n```clojure\n=\u003e (put! jedis 42 (Jedi. \"Hazel Caster\" \"vim\"))\n```\n\n```clojure\n=\u003e (select vim \"*\")\n\n#{#object[chazel.jedis.Jedi 0x7f518c04 \"{:name Luke Skywalker :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x4fd2044b \"{:name Hazel Caster :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x206c812 \"{:name Yoda :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x237fbc94 \"{:name Obi-Wan Kenobi :editor vim}\"]}\n```\n\n```clojure\n=\u003e (remove! jedis 5) ;; removing \"Luke Skywalker\"\n```\n```clojure\n=\u003e (select vim \"*\")\n#{#object[chazel.jedis.Jedi 0x1a7320c0 \"{:name Obi-Wan Kenobi :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x695908a1 \"{:name Yoda :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x4d51aba4 \"{:name Hazel Caster :editor vim}\"]}\n```\n\nNice, `vim` is a pretty \"view\" that is also \"materialized\"\n\n### Is Fast\n\nContinuous query cache can be created on a cluster member as well as on a cluster client. And when it is created on the client, given that there is enough memory to keep the cache, it really gains client a lot of performance.\n\nLet's connect as a client to a remote cluster (that has Jedi type on its classpath):\n\n```clojure\n=\u003e (def client (client-instance {:hosts [\"remote-hz-cluster.host\"] :cluster-name \"dev\"}))\n#'chazel/client\n```\n\nand work with this remote \"jedis\" map:\n\n```clojure\n=\u003e (def jedis (hz-map \"jedis\" client))\n#'chazel/jedis\n```\n\nMeasure the time it takes to run \"a where editor = vim\" query remotely:\n\n```clojure\n=\u003e (time (select jedis \"editor = vim\"))\n\n#{#object[chazel.jedis.Jedi 0x28a045c4 \"{:name Obi-Wan Kenobi :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x2b64eda6 \"{:name Hazel Caster :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x566f6bd7 \"{:name Yoda :editor vim}\"]}\n\n\"Elapsed time: 36.261975 msecs\"\n```\n\nCreate a query cache with the same predicate:\n\n```clojure\ndev=\u003e (def vim (query-cache jedis \"vim-cache\" \"editor = vim\"))\n#'chazel/vim\n```\n\nand time it:\n\n```clojure\n=\u003e (time (select vim \"*\"))\n\n#{#object[chazel.jedis.Jedi 0x312ee7e0 \"{:name Yoda :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x5ddb17e1 \"{:name Hazel Caster :editor vim}\"]\n  #object[chazel.jedis.Jedi 0x3aa47722 \"{:name Obi-Wan Kenobi :editor vim}\"]}\n\n\"Elapsed time: 0.355571 msecs\"\n```\n\nmore than 100 times faster: it's local _and continuous_.\n\n## Near Cache\n\nNear Cache is highly recommended for data structures that are mostly read. The idea is to bring data closer to the caller, and keep it in sync with the source.\n\nHere is from the official [Near Cache docs](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#near-cache):\n\n\u003e Map or Cache entries in Hazelcast are partitioned across the cluster members. Hazelcast clients _do not have local data at all_. Suppose you read the key k a number of times from a Hazelcast client or k is owned by another member in your cluster. Then each `map.get(k)` or `cache.get(k)` will be a remote operation, which creates a lot of network trips. If you have a data structure that is mostly read, then you should consider creating a local Near Cache, so that reads are sped up and less network traffic is created.\n\nNear Cache can be configured on the client as well as on the server (on a particular member). The configuration can be done via XML or programmatically. `chazel` adds a conveniece of an [EDN](https://github.com/edn-format/edn) based config.\n\nFor example an XML Near Cache config:\n\n```xml\n\u003cnear-cache name=\"myDataStructure\"\u003e\n  \u003cin-memory-format\u003e(OBJECT|BINARY|NATIVE)\u003c/in-memory-format\u003e\n  \u003cinvalidate-on-change\u003e(true|false)\u003c/invalidate-on-change\u003e\n  \u003ctime-to-live-seconds\u003e(0..INT_MAX)\u003c/time-to-live-seconds\u003e\n  \u003cmax-idle-seconds\u003e(0..INT_MAX)\u003c/max-idle-seconds\u003e\n  \u003ceviction eviction-policy=\"(LRU|LFU|RANDOM|NONE)\"\n            max-size-policy=\"(ENTRY_COUNT\n              |USED_NATIVE_MEMORY_SIZE|USED_NATIVE_MEMORY_PERCENTAGE\n              |FREE_NATIVE_MEMORY_SIZE|FREE_NATIVE_MEMORY_PERCENTAGE\"\n            size=\"(0..INT_MAX)\"/\u003e\n  \u003ccache-local-entries\u003e(false|true)\u003c/cache-local-entries\u003e\n  \u003clocal-update-policy\u003e(INVALIDATE|CACHE_ON_UPDATE)\u003c/local-update-policy\u003e\n  \u003cpreloader enabled=\"(true|false)\"\n             directory=\"nearcache-example\"\n             store-initial-delay-seconds=\"(0..INT_MAX)\"\n             store-interval-seconds=\"(0..INT_MAX)\"/\u003e\n\u003c/near-cache\u003e\n```\n\nor a Java based config:\n\n```java\nEvictionConfig evictionConfig = new EvictionConfig()\n  .setMaxSizePolicy(MaxSizePolicy.ENTRY_COUNT\n    |USED_NATIVE_MEMORY_SIZE|USED_NATIVE_MEMORY_PERCENTAGE\n    |FREE_NATIVE_MEMORY_SIZE|FREE_NATIVE_MEMORY_PERCENTAGE);\n  .setEvictionPolicy(EvictionPolicy.LRU|LFU|RANDOM|NONE);\n  .setSize(0..INT_MAX);\n\nNearCachePreloaderConfig preloaderConfig = new NearCachePreloaderConfig()\n  .setEnabled(true|false)\n  .setDirectory(\"nearcache-example\")\n  .setStoreInitialDelaySeconds(0..INT_MAX)\n  .setStoreIntervalSeconds(0..INT_MAX);\n\nNearCacheConfig nearCacheConfig = new NearCacheConfig()\n  .setName(\"myDataStructure\")\n  .setInMemoryFormat(InMemoryFormat.BINARY|OBJECT|NATIVE)\n  .setInvalidateOnChange(true|false)\n  .setTimeToLiveSeconds(0..INT_MAX)\n  .setMaxIdleSeconds(0..INT_MAX)\n  .setEvictionConfig(evictionConfig)\n  .setCacheLocalEntries(true|false)\n  .setLocalUpdatePolicy(LocalUpdatePolicy.INVALIDATE|CACHE_ON_UPDATE)\n  .setPreloaderConfig(preloaderConfig);\n```\n\nwith `chazel` would look like:\n\n```clojure\n{:in-memory-format :BINARY,\n :invalidate-on-change true,\n :time-to-live-seconds 300,\n :max-idle-seconds 30,\n :cache-local-entries true,\n :local-update-policy :CACHE_ON_UPDATE,\n :preloader {:enabled true,\n             :directory \"nearcache-example\",\n             :store-initial-delay-seconds 15,\n             :store-interval-seconds 60},\n :eviction  {:eviction-policy :LRU,\n             :max-size-policy :ENTRY_COUNT,\n             :size 800000}}\n```\n\nand can be passed directly to the client or server Hazelcast instance.\n\n### Client Near Cache\n\nOn the client Near Cache can be passed via a `:near-cache` key. For example:\n\n```clojure\n(client-instance {:near-cache {:name \"events\"}})\n```\n\nwould create a Hazelcast client instance with Near Cache configured for a map named `\"events\"`.\n\nSince only `:name` is provided in this case all the other Near Cache values will be created with Hazelcast defaults.\n\nMore config options can be added of course, for example:\n\n```clojure\n(client-instance {:near-cache {:name \"events\"\n                               :time-to-live-seconds 300\n                               :eviction {:eviction-policy :LRU}}})\n```\n\nThis config can be combined with other client config options:\n\n```clojure\n(client-instance {:cluster-name \"dev\"\n                  :hosts [\"127.0.0.1\"]\n                  :near-cache {:name \"events\"\n                               :time-to-live-seconds 300\n                               :eviction {:eviction-policy :LRU}}})\n```\n\n### Server Near Cache\n\n`chazel` allows to compose configurations that will be passed to the cluster on startup:\n\n```clojure\n(cluster-of 3 :conf (-\u003e\u003e (connect-to {:cluster-name \"foo\"})\n                         (with-near-cache {:in-memory-format :OBJECT\n                                           :local-update-policy :CACHE_ON_UPDATE\n                                           :preloader {:enabled true}}\n                                          \"events\")))\n```\n\nThis would create a cluster of 3 nodes with credentials and Near Cache for a map called `\"events\"`.\n\n## Distributed Tasks\n\nSending work to be done remotely on the cluster is very useful, and Hazelcast has a [rich set of APIs](http://docs.hazelcast.org/docs/3.5/javadoc/com/hazelcast/core/IExecutorService.html) to do that.\n\nchazel does not implement all the APIs, but it does provide a simple way of sending tasks to be executed remotely on the cluster:\n\n```clojure\n(task do-work)\n```\n\ndone.\n\n`task` here is a chazel's built-in function, and `do-work` is your function.\n\nA couple of gotchas:\n\n* `do-work` must exist on both: sending and \"doing the work\" JVMs\n* in case you'd like to pass a function with arguments use `partial`\n\n```clojure\n(task (partial do-work arg1 arg2 ..))\n```\n\n### Sending Runnables\n\nIn example above `do-work` gets wrapped into a Runnable internal chazel [Task](https://github.com/tolitius/chazel/blob/6bfd0275239ea96a9240efb7abed6adaafd8ee6d/src/chazel/chazel.clj#L194)\nand gets send to the cluster to execute.\n\nSay the function we are sending is:\n\n```clojure\n(defn do-work [\u0026 args]\n  (println \"printing remotely...\" args)\n  (str \"doing work remotely with args: \" args))\n```\n\nIf we send it with `(task do-work)`, you'll see `printing remotely... nil` in logs of a cluster member that picked up this task.\nBut you won't see `doing the work...` since it was silently executed on that member.\n\n### Sending Callables\n\nIn case you do want to know when the task is done, or you'd like to own the result of the tasks, you can send a task that will return you a future back.\nchazel calls this kind of task an `ftask`:\n\n```clojure\nchazel=\u003e (ftask do-work)\n#\u003cClientCancellableDelegatingFuture com.hazelcast.client.util.ClientCancellableDelegatingFuture@6148ce19\u003e\n```\n\nIn case of `ftask` chazel also wraps a function `do-work` into  [Task](https://github.com/tolitius/chazel/blob/6bfd0275239ea96a9240efb7abed6adaafd8ee6d/src/chazel/chazel.clj#L194), but now it cares of Task's Callable skills, hence we get a tasty future back. Let's deref it:\n\n```clojure\nchazel=\u003e @(ftask do-work)\n\"doing work remotely with args: \"\n```\n\nand send it some args:\n\n```clojure\nchazel=\u003e @(ftask (partial do-work 42 \"forty two\"))\n\"doing work remotely with args: (42 \\\"forty two\\\")\"\n```\n\n### Task Knobs\n\n#### Send to All\n\nA task that is sent with `task` of `ftask` by default will be picked up by any one member to run it.\nSometimes it is needed to send a task to be executed on all of the cluster members:\n\n```clojure\nchazel=\u003e (ftask (partial do-work 42 \"forty two\") :members :all)\n{#\u003cMemberImpl Member [192.168.1.4]:5702\u003e #\u003cClientCancellableDelegatingFuture com.hazelcast.client.util.ClientCancellableDelegatingFuture@2ae5cde4\u003e,\n #\u003cMemberImpl Member [192.168.1.4]:5701\u003e #\u003cClientCancellableDelegatingFuture com.hazelcast.client.util.ClientCancellableDelegatingFuture@7db6db4\u003e}\n```\n\nhere we have a small local two node cluster, and what comes back is a {member future} map. Let's get all the results:\n\n```clojure\nchazel=\u003e (def work (ftask (partial do-work 42 \"forty two\") :members :all))\n#'chazel/work\n\nchazel=\u003e (into {} (for [[m f] work] [m @f]))\n{#\u003cMemberImpl Member [192.168.1.4]:5702\u003e\n \"doing work remotely with args: (42 \\\"forty two\\\")\",\n #\u003cMemberImpl Member [192.168.1.4]:5701\u003e\n \"doing work remotely with args: (42 \\\"forty two\\\")\"}\n```\n\n#### Instance\n\nBy default chazel will look for a client instance, if it is active, it will use that, if not it will get a server instance instead.\nBut in case you'd like to use a concrete instance in order to send out tasks from you can:\n\n```clojure\n(task do-work :instance your-instance)\n```\n\n#### Executor Service\n\nBy default chazel will use a `\"default\"` executor service to submit all the tasks to.\nBut in case you'd like to pick a different one, you can:\n\n```clojure\n(task do-work :exec-svc-name \"my-es\")\n```\n\n#### All Together\n\nAll the options can be used with `task` and `ftask`:\n\n```clojure\n(task do-work :instance my-instance :exec-svc-name \"my-es\")\n```\n\n```clojure\n(ftask do-work :instance my-instance :members :all :exec-svc-name \"my-es\")\n```\n\n\u003cdiv id=\"reliable-topic\"/\u003e\n\n## Distributed Reliable Topic\n\nHazelcast's Reliable Topic is backed by a [Ringbuffer](http://blog.hazelcast.com/ringbuffer-data-structure/) which amongst other benefits (i.e. not destructive operations, ttl, batching, etc.) sequences all the messages, which allows for an interesting replay use cases.\n\nSince this is Hazelcast, we are dealing with a cluster of nodes, and depending on `backup-count` (a.k.a. quorum) this reliable topic is well _distributed_, which means it allows for better locality as well as higher availability: i.e. cluster may lose nodes, but all the topic messages will be still there to consume.\n\n### Processing Payments\n\nLet's say we have a system that publishes payments. We can send these payments to a reliable topic, and have some consumers that would be responsible to process these payments. So let's create this reliable topic:\n\n```clojure\nchazel=\u003e (def payments (hz-reliable-topic :payments))\n#'chazel/payments\n```\n\nand a simple functions that would process a single payment:\n\n```clojure\nchazel=\u003e (defn process-payment [p] (info \"processing payment\" p))\n#'chazel/process-payment\n```\n\nWe can now add this function as one of the topic listeners by calling `add-message-listener` on the topic:\n\n```clojure\nchazel=\u003e (add-message-listener payments process-payment)\n#uuid \"f3216455-f9c8-46ef-976a-cae942b15a8d\"\n```\n\nThis listener UUID can later be used to `remove-message-listener`.\n\nNow let's publish some payments:\n\n```clojure\nchazel=\u003e (publish payments {:name \"John\" :amount 4200.42M})\n\nINFO: processing payment {:name John, :amount 4200.42M}\n\nchazel=\u003e (publish payments {:name \"Kevin\" :amount 2800.28M})\n\nINFO: processing payment {:name Kevin, :amount 2800.28M}\n\nchazel=\u003e (publish payments {:name \"Jessica\" :amount 3400.34M})\n\nINFO: processing payment {:name Jessica, :amount 3400.34M}\n```\n\nYou can see that each payment is picked up by the listener and processed.\n\n### Replaying Events\n\nSo far so good, but not much different from a regular pub/sub topic. Let's make it more interesting.\n\nSay we have some problems with payments and we need to audit every payment that was sent. With a regular topic it would be hard to do (if at all possible) since we need to audit _all_ the payments: from the past and ongoing. With Hazelcast's Reliable Topic is not an issue, since it is backed by a Ringbuffer and all the messages are sequenced, we can just ask to replay the messages from an arbitrary sequence number.\n\nFirst let's create a function that will do the audit work:\n\n```clojure\nchazel=\u003e (defn audit-payment [p] (info \"auditing payment\" p))\n#'chazel/audit-payment\n```\n\nand add it as a _reliable_ listener:\n\n```clojure\nchazel=\u003e (add-reliable-listener payments audit-payment {:start-from 0})\n\"d274fab1-7f0f-47f9-a53a-58b35a4c68d1\"\n\nINFO: auditing payment {:name John, :amount 4200.42M}\nINFO: auditing payment {:name Kevin, :amount 2800.28M}\nINFO: auditing payment {:name Jessica, :amount 3400.34M}\n```\n\nInteresting, you see what happened? All the payments starting from the sequence `0` (the very beginning) were simply replayed and audited: niice!\n\nLet's publish more payments:\n\n```clojure\nchazel=\u003e (publish payments {:name \"Rudolf\" :amount 1234.56M})\n\nINFO: auditing payment {:name Rudolf, :amount 1234.56M}\nINFO: processing payment {:name Rudolf, :amount 1234.56M}\n\nchazel=\u003e (publish payments {:name \"Nancy\" :amount 6543.21M})\n\nINFO: auditing payment {:name Nancy, :amount 6543.21M}\nINFO: processing payment {:name Nancy, :amount 6543.21M}\n```\n\nNow _every_ ongoing payment gets processed _and_ audited, since there are two listeners attached to a topic.\n\nLet's replay them all again, just for fun:\n\n```clojure\nchazel=\u003e (add-reliable-listener payments audit-payment {:start-from 0})\n\"e2bd4912-7ccb-48b7-8102-b31e5660f68d\"\n\nINFO: auditing payment {:name John, :amount 4200.42M}\nINFO: auditing payment {:name Kevin, :amount 2800.28M}\nINFO: auditing payment {:name Jessica, :amount 3400.34M}\nINFO: auditing payment {:name Rudolf, :amount 1234.56M}\nINFO: auditing payment {:name Nancy, :amount 6543.21M}\n```\n\nniice!\n\n_there are other options that can be provided to a reliable listener: i.e. `start-from` `store-seq` `loss-tolerant?` `terminal?` if needed_\n\n## Map Event Listeners\n\nHazelcast has map entry listeners which can be attached to maps and listen on different operations, namely:\n\n* entry added\n* entry updated\n* entry removed\n* entry evicted\n* entry expired\n* entry loaded\n* entry merged\n\nchazel has all 7 listeners available as wrapper functions and ready to roll:\n\n* entry-added-listener\n* entry-updated-listener\n* entry-removed-listener\n* entry-evicted-listener\n* entry-expired-listener\n* entry-loaded-listener\n* entry-merged-listener\n\nA chazel map entry listener would take a function and apply it every time the event takes place:\n\n```clojure\nchazel=\u003e (def m (hz-map \"appl\"))\n#'chazel/m\n\nchazel=\u003e (put! m 42 1)\n\nchazel=\u003e m\n{42 1}\n```\n\nnothing fancy, usual map business. now let's add an update listener:\n\n```clojure\nchazel=\u003e (def ul (entry-updated-listener (fn [k v ov] (println \"updated: \" {:k k :v v :ov ov}))))\n#'chazel/ul\nchazel=\u003e (def id (add-entry-listener m ul))\n#'chazel/id\nchazel=\u003e id\n\"927b9530-630c-4bbb-995f-9c74815d9ca9\"\nchazel=\u003e\n```\n\n`ov` here is an `old value` that is being updated.\n\nWhen the listener is added, hazelcast assigns a `uuid` to it. We'll use it a bit later. For now let's see how the listener works:\n\n```clojure\nchazel=\u003e (put! m 42 2)\n1\nupdated:  {:k 42, :v 2, :ov 1}\nchazel=\u003e\n\nchazel=\u003e (put! m 42 3)\nupdated:  {:k 42, :v 3, :ov 2}\n2\n```\n\nnow every time an entry gets updated a function we created above gets applied.\n\nSince we have listener id, we can use it to remove this listener from the map:\n\n```clojure\nchazel=\u003e (remove-entry-listener m id)\ntrue\n\nchazel=\u003e (put! m 42 4)\n3\nchazel=\u003e m\n{42 4}\n```\n\nall back to vanilla, no listeners involved, map business.\n\n## Serialization\n\nSerialization is a big deal when hazelcast nodes are distributed, or when you connect to a remote hazelcast cluster.\nchazel solves this problem by delegating it to an optional serializer.\n\nTo start off, chazel has a [transit](https://github.com/cognitect/transit-clj) seriailzer ready to go:\n\n```clojure\nuser=\u003e (require '[chazel.serializer :refer [transit-in transit-out]])\nuser=\u003e (def m (hz-map \"amzn\"))\n\nuser=\u003e (put! m \"bids\" {:opening [429 431 430 429] :nbbo [428 430 429 427]} transit-out)\n#\u003cbyte[] [B@5d9d8664\u003e\nuser=\u003e\n```\nnotice `transit-out`, it is an optional function to `put!` that will be applied to the value before the hazelcast `.put` is called. In this case a value will be serialized with transit.\n\n```clojure\nuser=\u003e (cget m \"bids\")\n#\u003cbyte[] [B@638b6eec\u003e\n```\n\na default chazel's `cget` will return the value the way hazelcast has it stored: as a byte array. Similarly to `put!`, `cget` also takes in an optional function that is applied after the value is fetched from hazelcast:\n\n```clojure\nuser=\u003e (cget m \"bids\" transit-in)\n{:opening [429 431 430 429], :nbbo [428 430 429 427]}\n\nuser=\u003e (type (cget m \"bids\" transit-in))\nclojure.lang.PersistentArrayMap\n```\n\nIn case you need to use a different serializer, you can either send a pull request updating [chazel.serializer](https://github.com/tolitius/chazel/blob/master/src/chazel/serializer.clj), or by specifying your own \"secret\" serialize function in `put!` and `cget`.\n\n## Stats\n\nThis is a constant area of improvement and at the moment there are 2 ways to get some stats:\n\n### Maps and Sizes\n\nFirst is a simplistic way to find all the maps accross the cluster with their sizes (i.e. total number of values across all nodes):\n\n```clojure\nchazel=\u003e (def appl (hz-map \"appl\"))\n#'chazel/appl\nchazel=\u003e (def goog (hz-map \"goog\"))\n#'chazel/goog\n\nchazel=\u003e (map-sizes)\n{\"goog\" {:size 0}, \"appl\" {:size 0}}\n\nnow let's add some values and run `(map-sizes)` again:\n\nchazel=\u003e (doseq [n (range 2048)] (put! goog n (str n)))\nchazel=\u003e (doseq [n (range 1024)] (put! appl n (str n)))\n\nchazel=\u003e (map-sizes)\n{\"goog\" {:size 2048}, \"appl\" {:size 1024}}\n```\n\nnot too much intel, but proves to be quite useful: you see all the existing maps (IMap distributed objects) as well as their sizes.\n\n### Cluster Stats\n\nIn case you need to get _all_ stats across the cluster, there are options:\n\n* [Management Center](https://hazelcast.com/products/management-center/) that comes with hazelcast, but _you pay_ for clusters over 2 nodes\n* [hface](https://github.com/tolitius/hface) will give you all the stats with GUI, free for any number of nodes, but not as powerful as the management center\n* built in chazel `(cluster-stats)` function, but you'll have to include an [8KB dependency](https://clojars.org/org.hface/hface-client) to your cluster nodes\nwhich is just a callable that is able to collect node stats\n\nHere is an example of a built in `(cluster-stats)`:\n\n```clojure\n\nchazel=\u003e (cluster-stats)\n\n{\"Member [192.168.1.185]:5701 this\"\n {:master true,\n  :clusterName \"dev\",\n  :instanceNames [\"c:goog\" \"c:appl\" \"e:stats-exec-service\"],\n  :memberList\n  [\"192.168.1.185:5701\" \"192.168.2.185:5702\" \"192.168.2.185:5703\"],\n  :memberState\n  {:runtimeProps\n   {:osMemory.freePhysicalMemory 2046976000,\n    :runtime.loadedClassCount 10130,\n    ;;...\n    }}\n   :executorStats {:stats-exec-service {:creationTime 1462910619108, :pending 0, :started 4, :completed 3, :cancelled 0, :totalStartLatency 0, :totalExecutionTime 49}},\n   :multiMapStats {},\n   :topicStats {},\n   :memoryStats {:committedNativeMemory 0, :creationTime 0, :usedNativeMemory 0, :freePhysical 2046976000, :maxNativeMemory 0, :freeNativeMemory 0, :maxHeap 3817865216, :totalPhysical 17179869184, :usedHeap 985153872, :gcStats {:creationTime 0, :minorCount 17, :minorTime 198, :majorCount 2, :majorTime 314, :unknownCount 0, :unknownTime 0}, :committedHeap 1548746752},\n   :mapStats\n   {:goog\n    {:creationTime 1462910602378, :maxGetLatency 0, :maxPutLatency 2, :lastAccessTime 0, :maxRemoveLatency 0, :heapCost 238277, :totalGetLatencies 0, :numberOfOtherOperations 90, :ownedEntryMemoryCost 118788, :getCount 0, :hits 0, :backupCount 1, :totalRemoveLatencies 0, :backupEntryMemoryCost 119489, :removeCount 0, :totalPutLatencies 316, :dirtyEntryCount 0, :lastUpdateTime 1462910608301, :backupEntryCount 681, :lockedEntryCount 0, :ownedEntryCount 677, :putCount 2048, :numberOfEvents 0},\n    :appl\n    {:creationTime 1462910599320, :maxGetLatency 0, :maxPutLatency 68, :lastAccessTime 0, :maxRemoveLatency 0, :heapCost 119125, :totalGetLatencies 0, :numberOfOtherOperations 90, :ownedEntryMemoryCost 60004, :getCount 0, :hits 0, :backupCount 1, :totalRemoveLatencies 0, :backupEntryMemoryCost 59121, :removeCount 0, :totalPutLatencies 390, :dirtyEntryCount 0, :lastUpdateTime 1462910604627, :backupEntryCount 338, :lockedEntryCount 0, :ownedEntryCount 343, :putCount 1024, :numberOfEvents 0}},\n   :replicatedMapStats {},\n   :queueStats {},\n   ;; lots and lots more for this member..\n }\n\n \"Member [192.168.2.185]:5703\"\n {:master false,\n  :clusterName \"dev\",\n  :instanceNames [\"c:goog\" \"c:appl\" \"e:stats-exec-service\"],\n  ;; lots and lots more for this member..\n }\n\n \"Member [192.168.2.185]:5702\"\n {:master false,\n  :clusterName \"dev\",\n  :instanceNames [\"c:goog\" \"c:appl\" \"e:stats-exec-service\"],\n   ;; lots and lots more for this member..\n }\n```\n\n`(cluster-stats)` returns a `{member stats}` map with ALL the stats available for the cluster.\n\nagain in order to make it work, add a little [8KB dependency](https://clojars.org/org.hface/hface-client) to your cluster nodes, so it can collect stats\nfrom each node / member.\n\n## License\n\nCopyright © 2020 tolitius\n\nDistributed under the Eclipse Public License either version 1.0 or (at\nyour option) any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftolitius%2Fchazel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftolitius%2Fchazel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftolitius%2Fchazel/lists"}