{"id":20038037,"url":"https://github.com/perfectlysoft/perfect-zookeeper","last_synced_at":"2025-05-05T06:32:00.818Z","repository":{"id":69670194,"uuid":"82343960","full_name":"PerfectlySoft/Perfect-ZooKeeper","owner":"PerfectlySoft","description":"A ZooKeeper Client in Swift (LINUX ONLY)","archived":false,"fork":false,"pushed_at":"2018-02-09T17:28:42.000Z","size":60,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-08T18:51:20.380Z","etag":null,"topics":["cluster","clustered-servers","clustering-algorithm","zookeeper"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PerfectlySoft.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-17T22:33:05.000Z","updated_at":"2024-05-02T06:06:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"8dbdb4ac-4de1-42dc-a9b7-23d90de0994b","html_url":"https://github.com/PerfectlySoft/Perfect-ZooKeeper","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/PerfectlySoft%2FPerfect-ZooKeeper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-ZooKeeper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-ZooKeeper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-ZooKeeper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PerfectlySoft","download_url":"https://codeload.github.com/PerfectlySoft/Perfect-ZooKeeper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252451618,"owners_count":21749958,"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":["cluster","clustered-servers","clustering-algorithm","zookeeper"],"created_at":"2024-11-13T10:24:52.742Z","updated_at":"2025-05-05T06:32:00.801Z","avatar_url":"https://github.com/PerfectlySoft.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Perfect-ZooKeeper[简体中文](README.zh_CN.md)\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"http://perfect.org/get-involved.html\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://perfect.org/assets/github/perfect_github_2_0_0.jpg\" alt=\"Get Involed with Perfect!\" width=\"854\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/PerfectlySoft/Perfect\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/Perfect_GH_button_1_Star.jpg\" alt=\"Star Perfect On Github\" /\u003e\n    \u003c/a\u003e  \n    \u003ca href=\"http://stackoverflow.com/questions/tagged/perfect\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/perfect_gh_button_2_SO.jpg\" alt=\"Stack Overflow\" /\u003e\n    \u003c/a\u003e  \n    \u003ca href=\"https://twitter.com/perfectlysoft\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/Perfect_GH_button_3_twit.jpg\" alt=\"Follow Perfect on Twitter\" /\u003e\n    \u003c/a\u003e  \n    \u003ca href=\"http://perfect.ly\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://www.perfect.org/github/Perfect_GH_button_4_slack.jpg\" alt=\"Join the Perfect Slack\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://developer.apple.com/swift/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Swift-3.0-orange.svg?style=flat\" alt=\"Swift 3.0\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://developer.apple.com/swift/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Platforms-OS%20X%20%7C%20Linux%20-lightgray.svg?style=flat\" alt=\"Platforms OS X | Linux\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://perfect.org/licensing.html\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/License-Apache-lightgrey.svg?style=flat\" alt=\"License Apache\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://twitter.com/PerfectlySoft\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Twitter-@PerfectlySoft-blue.svg?style=flat\" alt=\"PerfectlySoft Twitter\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://perfect.ly\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://perfect.ly/badge.svg\" alt=\"Slack Status\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\n\nThis project implements an express Swift library of ZooKeeper\n\nThis package builds with Swift Package Manager and is part of the [Perfect](https://github.com/PerfectlySoft/Perfect) project.\n\n\n## Release Note\n\nThis project can only be built on ⚠️ Ubuntu 16.04 ⚠️ . Mac OS X doesn't support it.\n\n## Quick Start\n\n### Swift Package Manager\n\nAdd Perfect-ZooKeeper to your project's Package.swift:\n\n``` swift\n.Package(url: \"https://github.com/PerfectlySoft/Perfect-ZooKeeper.git\", majorVersion: 1)\n```\n\n### Import Library\n\nImport Perfect-ZooKeeper library to your source code:\n\n``` swift\nimport PerfectZooKeeper\n```\n\n### Debug\n\nTo debug your ZooKeeper application, Perfect-ZooKeeper provides a static method called `debug` to set the debug level, for example:\n\n``` swift\n// this will set debug level to the whole application\nZooKeeper.debug()\n```\n\nYou can also adjust the debug level to method `debug(_ level: LogLevel = .DEBUG)` by a parameter:\n\n- level: LogLevel, the debug level, could be .ERROR, .WARN, .INFO, or .DEBUG by default\n\n### Log\n\nTo trace and log your ZooKeeper application, Perfect-ZooKeeper provides a static method called `log`, for example:\n\n``` swift\n\n// this will redirect the debug information to standard error stream\nZooKeeper.log()\n\n```\n\nThe only parameter of `log(_ to: UnsafeMutablePointer\u003cFILE\u003e = stderr)` is `to`, a `FILE` pointer as in C stream, it is `stderr` by default but you can redirect it to any available `FILE` streams.\n\n### Using ZooKeeper Object\n\nBefore performing any actual connections, it is necessary to construct a `ZooKeeper` object:\n\n``` swift\nlet z = ZooKeeper()\n```\n\nOr alternatively, you can also add a timeout setting to such an object, which defines the maximal milliseconds to wait for connection:\n\n``` swift\n// indicates that the connection attempt will be treated as broken in eight seconds.\nlet z = ZooKeeper(8192)\n```\n\n### Connect to ZooKeeper Hosts\n\nUse `ZooKeeper.connect()` to connect to specified hosts. Take example, the demo below shows how to connect to a ZooKeeper host, and how the program invokes your callback once connected:\n\n``` swift\ntry z.connect(\"servername:2181\") { connect in\n  switch(connect) {\n  case .CONNECTED:\n    // connection is made\n  case .EXPIRED:\n    // connection is expired\n  default:\n    // connection is broken\n  }\n}\n```\n\n⚠️ NOTE ⚠️ , you may also connect to a cluster of host by replacing the above connection string into a string of multiple hosts in such an expression: `\"server1:2181,server2:2181,server3:2181\"`, but this may be subject to the ZooKeeper version that you are connecting with. For more information of ZooKeeper connection string, see [ZooKeeper Programmer's Guide](https://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html)\n\n\n### Existence of a ZNode\n\nOnce connected, you may check a specific ZNode by calling `exists()`, for example:\n\n``` swift\nlet a = try z.exists(\"/path/to\")\nprint(a)\n```\n\nThis function will return a `Stat()` structure if nothing wrong, for example:\n``` swift\n// this is a sample result of calling print(try z.exists(\"/path/to\"))\nStat(czxid: 0, mzxid: 0, ctime: 0, mtime: 0, version: 0, cversion: -1, aversion: 0, ephemeralOwner: 0, dataLength: 0, numChildren: 1, pzxid: 0)\n```\n\n### List Children of a ZNode\n\nMethod `children()` may list all available direct sub nodes under the objective and put them into an array of string.\n\n``` swift\nlet kids = try z.children(\"/path/to\")\n// if success, it will list all sub nodes under /path/to in an array.\n// for example, if there is /path/to/a and /path/to/b,\n// then the result is probably [\"a\", \"b\"]\nprint(kids)\n```\n\n### Save Data to a ZNode\n\nAs a key-value directory, each ZNode may contain a small amount of data in form of a string, usually not exceed to 10k. You can save your own configuration data into a ZNode, synchronously or asynchronously, as demanded.\n\n#### Save Data Synchronously\n\nSynchronous version of `save()` will return a `Stat()` structure if success:\n\n``` swift\nlet stat = try z.save(\"/path/to/key\", data: \"my configuration value of key\")\nprint(stat)\n```\n\nParameters of `func save(_ path: String, data: String, version: Int = -1) throws -\u003e Stat`:\n- path: String, the absolute full path of the node to access\n- data: String, the data to save\n- version: Int, version of data, default is -1 which indicates ignoring the version info\n\n#### Save Data Asynchronously\n\nAsynchronous version of `save()` has all the same parameters with an extra `StatusCallback` but without returning value:\n\n``` swift\ntry z.save(\"/path/to/key\", data: \"my configuration value of key\") { err, stat in\n  guard err == .ZOK else {\n    // something wrong\n  }\n  guard let st = stat else {\n    // async save() returns a null status\n  }\n  // print the status after saving\n  print(st)\n}\n```\n\n### Load Data from a ZNode\n\nSimilar to `save()`, the ZooKeeper `load()` also has both synchronous version and asynchronous version as well:\n\n#### Load Data Synchronously\n\nTo load data from a ZNode synchronously, simply call `load(\"/path/to\")`, and it will return a tuple of `(value: String, stat: Stat)`, which stands for data value and status of the node:\n\n``` swift\nlet (value, stat) = try z.load(\"/path/to\")\n```\n\n#### Load Data Asynchronously\n\nThe data loading from a ZNode asynchronously will require an extra callback with a parameter of (error: Exception, value: String, Stat) as demo below:\n\n``` swift\ntry z.load(path) { err, value, stat in\n  guard err == .ZOK else {\n    // something wrong\n  }//end guard\n  guard let st = stat else {\n    // there is no status information of node\n  }//end guard\n  print(st)\n  // this is the actual data value as a String\n  print(value)\n}//end load\n```\n\n### Make a Node\n\nFunction `func make(_ path: String, value: String = \"\", type: NodeType = .PERSISTENT, acl: ACLTemplate = .OPEN) throws -\u003e String` can build different type of nodes, with writing data value and set ACL (Access Control List) info for this node in the same moment. Here are the parameters:\n\n- path: String, the absolute full path of the node to make\n- value: String, the value to store into node\n- type: NodeType, i.e., .PERSISTENT, .EPHEMERAL, .SEQUENTIAL, or .LEADERSHIP, which means ephemeral + sequential. Default type is .PERSISTENT\n- acl: ACLTemplate, basic ACL template to apply in this incoming node, i.e., .OPEN, .READ or .CREATOR. Default is .OPEN, which means nothing to restrict\n\n⚠️ Note ⚠️ The return value will be the newly created pat, i.e., the same one as input if type is .PERSISTENT or .EPHEMERAL, and will be appended with a serial number only if the node type is .SEQUENTIAL or .LEADERSHIP.\n\n#### Make a Persistent Node\n\nThe following code demonstrates how to create a persistent node with data:\n\n``` swift\nlet _ = try z.make(\"/path/to/key\", value: \"my config data value for this key\")\n```\n\n#### Make a Temporary Node\n\nA temporary ZNode means it will automatically disappear once the session was over (usually after a few seconds of disconnection). To create such a node, simply add a node type parameter to call:\n\n``` swift\nlet _ = try z.make(\"/path/to/tempKey\", value: \"data for this temporary key\", type: .EPHEMERAL)\n```\n\n#### Make a Sequential Node\n\nA Sequential ZNode means if you want to create a `/path/to/key` node, it will return a `/path/to/key0123456789`, i.e., a 10 digit number will be added to the node you named.\n\n``` swift\nlet path = try z.make(\"/path/to/myApplication\", type: .SEQUENTIAL)\nprint(path)\n// if success, the path will be something like `/path/to/myApplication0000000123`\n```\n\n⚠️ Note ⚠️ Sequential node is persistent and can be removed only by calling `remove()` method explicitly.\n\n#### Make a Leadership Node\n\nThe purpose of leadership node is to select a leadership server among all candidates. Similar to .SEQUENTIAL, the leadership node is also a temporary node, which help all clustered backups to determine who shall be the leader among the cluster by checking whose serial number is the minimal one, which means who is the first available.\n\n``` swift\nlet path = try z.make(\"/path/to/myApplication\", type: .LEADERSHIP)\nprint(path)\n// if success, the path will be something like `/path/to/myApplication0000000123`\n// and will be deleted automatically once disconnected.\n```\n\n### Remove a Node\n\nMethod `func remove(_ path: String, version: Int32 = -1)` enables the function to delete an existing node:\n\n``` swift\n// this action will result in a removal regardless node versions.\ntry z.remove(\"/path/to/uselessNode\")\n```\n\n### Watch for Changes\n\nPerfect-ZooKeeper provides a useful function `watch()` to monitor other instance operations against a specific node.\nThe full API of `watch()` method is `func watch(_ path: String, eventType: EventType = .BOTH, renew: Bool = true, onChange: @escaping WatchCallback)` with parameter explained below:\n\n- path: String, the absolute full path of the node to watch\n- eventType: watch for .DATA or .CHILDREN, or .BOTH\n- renew: watch the event for once or for ever, false for once and true for ever.\n- onChange: WatchCallback, callback once something changed\n\nFor example:\n\n``` swift\ntry z.watch(\"/path/to/myCheese\") { event in\n  switch(event) {\n  case CONNECTED:\n    // me myself just connected to this node???\n  case DISCONNECTED:\n    // connection is broken\n  case EXPIRED:\n    // connection is expired\n  case CREATED:\n    // this shall never happen - just created for the node me watch?\n  case DELETED:\n    // the node has been deleted by someone else\n  case DATA_CHANGED:\n    // someone just touched my cheese\n  case CHILD_CHANGED:\n    // children were changed\n  default:\n    // unexpected here\n  }\n}//end watch\n```\n\n### Election in a Cluster\n\nPerfect ZooKeeper provides a convenient way of choosing a leader / master from a cluster by calling method `elect()`:\n\n``` swift\nlet (me, leader, candidates) = try z.elect(\"/path/to\")\n```\n\nIf success, the return value of `elect()` function is a tuple of `(me: Int, leader: Int, candidates: [Int])`, which means every candidate, include the current instance, will be included in the `candidates` array as an integer. Result `me` is the current instance's election serial number and the result of `leader` is the number represents the final leader in this election. If `me == leader`, then congratulations - the current instance of ZooKeeper just won the election. In such a case, further actions should be done to upgrade current instance into the master of cluster.\n\n### ACL Operations\n\nACL, the access control list, can be manipulated in ZooKeeper API by core data structure `ACL_vector`, i.e., a C based pointer array. Content of such a structure could be checked by similar operations as below:\n\n``` swift\nfunc show(_ aclArray: ACL_vector) {\n  guard let pAcl = aclArray.data else {\n    // the array doesn't contain any valid data\n    return\n  }\n  var i = 0\n  while (Int32(i) \u003c aclArray.count) {\n    let cursor = pAcl.advanced(by: i)\n    let acl = cursor.pointee\n    let scheme = String(cString: acl.id.scheme)\n    let id = String(cString: acl.id.id)\n    // id is subject to scheme\n    // if scheme is \"world\", then id shall be \"anyone\",\n    // scheme \"auth\" doesn't use any id.\n    // scheme \"ip\" uses host ip as id, such as \"1.2.3.4/5\"\n    // scheme \"x509\" uses client X500 principal as id.\n    // scheme \"digest\" use name:password to generate MD5 as id:\n    // `username:base64 encoded SHA1 password digest.`\n    print(\"id: \\(id)\")\n    print(\"scheme: \\(scheme)\")\n    // permission is a combination of :\n    // ZOO_PERM_READ | ZOO_PERM_WRITE | ZOO_PERM_CREATE\n    // ZOO_PERM_DELETE | ZOO_PERM_ADMIN | ZOO_PERM_ALL\n    let perm = String(format: \"%8X\", acl.perms)\n    print(\"permissions: \\(perm)\")\n    i += 1\n  }\n}\n```\n\n⚠️ CAUTION ⚠️ To manipulate ACL_vector with permission options, please make sure to import the C ZooKeeper library:\n\n``` swift\nimport czookeeper\n```\n\n#### Get ACL Info\n\nMethod `getACL()` can retrieve ACL info from a ZNode:\n\n``` swift\nlet (acl, stat) = try z.getACL(\"/path/to\")\n```\n\nThe return value is a tuple of `(acl: ACL_vector, stat: Stat)` stands for the acl info as an `ACL_vector` pointer array and status of the node.\n\n#### Set ACL Info\n\nMethod `func setACL(_ path: String, version: Int32 = -1, acl: ACL_vector) throws` may save an ACL setting to a ZNode, where the version could be skipped by default and providing a valid `ACL_vector` pointer array:\n\n``` swift\nvar acl = ACL_vector()\n// do some modification to acl variable\ntry z.setACL(\"/path/to\", acl:acl)\n```\n\nHowever, there is also another alternative form of `setACL()` by replacing the complicated `ACL_vector` to preset ACL template:\n\n``` swift\n// available templates include .OPEN, .READ and .CREATOR\n// default is .OPEN, which means nothing to restrict\ntry z.setACL(\"/path/to\", aclTemplate: .READ)\n```\n\n## Further Information\nFor more information on the Perfect project, please visit [perfect.org](http://perfect.org).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfectlysoft%2Fperfect-zookeeper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fperfectlysoft%2Fperfect-zookeeper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfectlysoft%2Fperfect-zookeeper/lists"}