{"id":16476382,"url":"https://github.com/softprops/zoey","last_synced_at":"2025-03-23T11:32:57.293Z","repository":{"id":57736631,"uuid":"13793762","full_name":"softprops/zoey","owner":"softprops","description":"taking scala to the zoo","archived":false,"fork":false,"pushed_at":"2015-02-09T04:04:00.000Z","size":524,"stargazers_count":29,"open_issues_count":3,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-17T12:00:06.257Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/softprops.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":"2013-10-23T04:49:00.000Z","updated_at":"2016-08-24T02:48:46.000Z","dependencies_parsed_at":"2022-08-24T14:57:19.774Z","dependency_job_id":null,"html_url":"https://github.com/softprops/zoey","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softprops%2Fzoey","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softprops%2Fzoey/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softprops%2Fzoey/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softprops%2Fzoey/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/softprops","download_url":"https://codeload.github.com/softprops/zoey/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245097158,"owners_count":20560311,"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","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":[],"created_at":"2024-10-11T12:42:17.862Z","updated_at":"2025-03-23T11:32:56.987Z","avatar_url":"https://github.com/softprops.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# zoey\n\n[![Build Status](https://travis-ci.org/softprops/zoey.svg)](https://travis-ci.org/softprops/zoey)\n\n\u003e taking Scala to the zoo\n\na non-blocking interface for [apache zookeeper](http://zookeeper.apache.org/). Inspired by Twitter's wonderful zk-client, implemented in terms of scala's standard library features.\n\n## goals\n\n* be simple\n\nThe underlying concepts of zookeeper are simple. Interacting with the standard java driver is not.\n\n* be robust\n\nZookeeper is a mature HA answer for many distributed systems questions. Despite that, networks are still unreliable. These zookeeper interfaces should be designed with that in mind.\n\n* be asyncronous by default\n\nThe standard java zookeeper driver supports blocking operations with an optional support for callback style async operations. Scala's standard library Futures model latter very well providing a familiar interface that works well with existing scala libraries. \n\n* model interfaces more closely to the domain\n\nThe standard java zookeeper driver defines one entry point, the client, which defines a number of methods for both managing a session's connection state and performing operations on paths. Zoey splits up this interface in a way that makes ZNodes, a core zookeeper abstraction, a first class citizen in zoey's client interface.\n\n## install\n\nadd the following to your sbt build definition\n\n```scala\nresolvers += \"softprops-maven\" at \"http://dl.bintray.com/content/softprops/maven\"\n\nlibraryDependencies += \"me.lessis\" %% \"zoey-core\" % \"0.1.2\"\n```\n\n## usage\n\n### zoey core\n\nZoey core defines interfaces for creating a configurable and robust client to communicate with zookeeper servers.\n\n#### Creating a client\n\nCreating a new client with default options is simple.\n\n```scala\nval cli = zoey.ZkClient()\n```\n\nThe default client will connect a zookeeper server listening on `0.0.0.0:2181`. If you've spun up a zookeeper locally\nthis may be fine, but if you've spun one up on another host just provide that connection string as an argument to your clients constructor.\n\n```scala\nval cli = zoey.ZkClient(connectString)\n```\n\nMany clients connecting to the same servers to perform operations can have an undesirable herding effect. Zookeeper servers are often distributed across a number of hosts for high availability. A _round robin_ interface is provided for creating a zookeeper client that will perform requests on a different host for each operation in the order the hosts are defined.\n\n```scala\nval cli = zoey.ZkClient.roundRobin(hostA :: hostB :: hostC :: Nil)\n```\n\nIf a given server is unavailable, you don't want to wait on a connection to establish all day. In these cases you may with to set a connection timeout, specified as a [FiniteDuration](http://www.scala-lang.org/api/current/index.html#scala.concurrent.duration.FiniteDuration).\n\n```scala\nimport scala.concurrent.duration._\nval cli = zoey.ZkClient(connectString, connectTimeout = Some(3 seconds))\n```\n\nOnce connected the server, the server will attempt to make sure its connection with you is healthy by establishing a session timeout for gaps in network connectivity. By default, this session timeout is set to 4 seconds. If you wish to change this, set the sessionTimeout when creating your client.\n\n```scala\nimport scala.concurrent.duration._\nval cli = zoey.ZkClient(connectStr, sessionTimeout = 10.seconds)\n```\n\nOnce you've created a client, you may which to configure it's [mode](http://zookeeper.apache.org/doc/r3.4.6/api/org/apache/zookeeper/CreateMode.html) of operation. Typically this will be one of ephemeral or persistent and optionally sequential.\nThe client mode defines the semantics for how long of information written to zookeeper is stored. The default is persistent.\n\nZoey's client interfaces produce immutable instances but who share a client connection. Keep this in mind when storing references to client instances.\n\n```scala\nval ephemeralSeqCli = cli.emphermalSequential\n```\n\nOperations requested over a distributed network system are not guaranteed to be successful due to a number of potential factors. To make zoey\nmore robust in the face of these potential issues, zoey provides an interface for retrying failed operations, leveraging interfaces defined in the [retry](https://github.com/softprops/retry) library.\n\nThe following will attempt to retry operations up to 4 times with an [exponential backoff](https://github.com/softprops/retry#backoff) time starting at 1 second.\n\n```scala\nval retryingCli = cli.retryWith(\n  retry.Backoff(max = 4, delay = 1.second))\n```\n\n### Getting data out\n\nZookeeper stores data with structures called ZNodes which are addressable by directory-like paths. To reference a ZNode, provide it's path\nto the client.\n\n```scala\nval node = cli.node(\"/foo/bar\")\n```\n\nor more simply...\n\n```scala\nval node = cli(\"/foo/bar\")\n```\n\nThe above `node` is a reference to a zookeepers ZNode's address. No information as been collected from or sent to the server yet. It is just a description for a future point of reference when invoking operations.\n\nZookeeper defines a basic set of operations for creating, updating and reading data from ZNodes.\n\nRead operations are unique in that you can request information, and optionally, request to get notified when this information changes.\n\nZookeeper calls these notifications \"watches\"\n\nFor example, given the znode reference we created above, `node`, we can ask zookeeper if this ZNode exists _and_ to get notified when it that fact changes.\n\nThese watch notifications only fire once, and thus, are perfectly represented as scala Futures which also may only be satisfied once.\n\n```scala\n// the `exists` operation\nval exists = node.exists\n\n// calling apply() on a read operation produces a future which will be satisfied once information is retried once\nval future = exists()\nfuture.foreach {\n  case exists =\u003e println(s\"rec $exists\")\n}\n\n// calling watch() on a read operation produces a structure that exposes of the result of the operation as a Try and a future \n// which will be satisfied when an update to this information occurs. Since futures may only be satisfied once, when\n// and update occurs you will need to re-request a new watch for the read operation on the ZNode\nval watch = exists.watch()\nwatch.foreach {\n  case zoey.Watch(exists, updateFuture) =\u003e\n    println(s\"exists $exists\")\n    updateFuture.foreach {\n      case event =\u003e onPathChanged(event)\n    }\n}\n```\n\nBesides knowing that a given ZNode exists, you can ask for what data is contain with the `data` operation.\n\n```scala\nnode.data().foreach {\n  case data =\u003e\n    // data is a znode ref populated with the bytes stored at its path\n    println(s\"node stores ${data.bytes.size} bytes\")\n}\n```\n\nZNodes differ from traditional file system file descriptors in that they can act as both containers for data _and_ directories which have a list of child paths which are addresses to other ZNodes.\n\n```scala\nnode.children().foreach {\n  case children =\u003e\n    // children is a znode ref populated with the nodes stored at its path\n    println(s\"node has ${children.nodes.size} children\")\n}\n```\n\nAn instance of [Ordering](http://www.scala-lang.org/api/current/index.html#scala.math.Ordering) is defined for ZNodes which accounts for [sequential](http://zookeeper.apache.org/doc/r3.4.6/api/org/apache/zookeeper/CreateMode.html#EPHEMERAL_SEQUENTIAL) [names](http://zookeeper.apache.org/doc/r3.4.6/api/org/apache/zookeeper/CreateMode.html#PERSISTENT_SEQUENTIAL). This is useful due to the fact that getting the children of a given znode defines [no guarantees](http://zookeeper.apache.org/doc/r3.4.6/api/org/apache/zookeeper/ZooKeeper.html#getChildren(java.lang.String, boolean)) about the order in which they are returned.\n\n### Getting data in\n\nNow that you know how to get information out of zookeeper. Let's look at how you get it in.\n\nZNode path addresses work just like UNIX paths. You can't create path \"/foo/bar\" is path \"/foo\" doesn't exist. In the UNIX world you can request the `-p` (parent) flag to the `mkdir` command to ensure parent directories get created. With zoey, if you are storing data addressed at path \"/foo/bar\" and\ndon't know for sure that ZNode \"/foo\" exists you can set the `parent` parameter to true on create requests\n\n```scala\nnode.create(\"test\".getBytes, parent = true)\n .foreach {\n   case created =\u003e println(s\"created $created\")\n }\n```\n\nWhat you can create, you can also update, but the current data a ZNode holds, as seen by you, may not be consistent with the data as seen\nby the zookeeper server if there are other zookeeper clients interacting with the server. To accommodate this consistency conundrum zookeeper\nattaches version information to ZNodes. You can read this version information by requesting the znode [Stat](http://zookeeper.apache.org/doc/r3.4.6/api/org/apache/zookeeper/data/Stat.html) with `znode.exists`.\n\n```scala\nnode.set(\"updated\".getBytes, version).foreach {\n  case result =\u003e println(s\"updated $result\")\n}\n```\n\n### Closing time\n\nSince a zoey ZkClient maintains a persistent connection with a zookeeper server, you should instrument your application in a way\nto gracefully terminate this connection. To do this. Simply call the `close` method defined on ZkClient which will return a Future that will be satisfied when the close operation is complete.\n\n```scala\nval closeFuture = zkClient.close()\ncloseFuture.foreach {\n  case _ =\u003e println(\"the zoo is closed\")\n}\n```\n\nClose should only be called once.\n\nDoug Tangren (softprops) 2013-2014\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftprops%2Fzoey","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoftprops%2Fzoey","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftprops%2Fzoey/lists"}