{"id":13747861,"url":"https://github.com/thebigredgeek/microlock","last_synced_at":"2025-04-05T16:09:18.900Z","repository":{"id":9825985,"uuid":"63469148","full_name":"thebigredgeek/microlock","owner":"thebigredgeek","description":"A dead simple distributed locking library for Node.js and Etcd","archived":false,"fork":false,"pushed_at":"2025-03-18T04:01:11.000Z","size":85,"stargazers_count":93,"open_issues_count":14,"forks_count":8,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T15:09:10.376Z","etag":null,"topics":["distributed-locks","etcd","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/thebigredgeek.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2016-07-16T06:33:58.000Z","updated_at":"2023-05-24T02:09:43.000Z","dependencies_parsed_at":"2024-01-15T00:52:28.723Z","dependency_job_id":"e8d7823b-b3f0-46a6-8a31-3f76e1705cf6","html_url":"https://github.com/thebigredgeek/microlock","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thebigredgeek%2Fmicrolock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thebigredgeek%2Fmicrolock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thebigredgeek%2Fmicrolock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thebigredgeek%2Fmicrolock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thebigredgeek","download_url":"https://codeload.github.com/thebigredgeek/microlock/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247361691,"owners_count":20926643,"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":["distributed-locks","etcd","nodejs"],"created_at":"2024-08-03T07:00:26.485Z","updated_at":"2025-04-05T16:09:18.882Z","avatar_url":"https://github.com/thebigredgeek.png","language":"JavaScript","funding_links":[],"categories":["Libraries","JavaScript"],"sub_categories":["Distributed locks"],"readme":"# Microlock\nA dead simple distributed locking library for Node.js and [etcd](http://github.com/coreos/etcd) (via [node-etcd](https://github.com/stianeikeland/node-etcd))\n\n[![NPM](https://nodei.co/npm/microlock.png)](https://nodei.co/npm/microlock/)\n\n[![CircleCI](https://circleci.com/gh/thebigredgeek/microlock.svg?style=shield)](https://circleci.com/gh/thebigredgeek/microlock/tree/master)\n\n\n\n\n## What is Etcd?\n\n[Etcd](https://github.com/coreos/etcd) is a distributed key-value store, built by the [CoreOS](https://coreos.com/) team, that provides strong guarantees around consistency and partition tolerance.  Data is duplicated to all nodes in a given cluster and remains consistent between node failures.  Cluster leaders are elected via the [Raft consensus algorithm](https://raft.github.io/).  Etcd provides operations for atomic value swapping/removal based on criteria and TTL for values, making it a perfect media for distributed locks.\n\n## What is a distributed lock?\n\nA distributed lock is a mechanism that provides serialized flow control on a context that is acted on by more than one process.  These processes typically operate on different machines via Service Oriented Architecture.  Each process uses an object called a distributed lock to \"lock\" access to the shared context, aliased by a key, so that only one process, each aliased by a node id, can act on it at a time, thereby ensuring consistency and preventing race conditions.\n\n## Why not Redlock?\n\nRedis is great for a lot of things.  Caching, keeping processes stateless, and fast access to simply structured data are all cases where Redis shines.  However, implementing a distributed lock with Redis via Redlock has several caveats that are unsuitable for many cases.  Namely, if you need strong guarantees that a lock will not be acquired by multiple nodes at once even in the event of failure, Redlock isn't a viable option.\n\n## Notes\n\nMicrolock is currently compatible with Etd 2.2.x.  Work on support for Etcd 2.2.x - 3.x is in progress.\n\n## Install\n**Requires NodeJS \u003e= 4.0**\n\n```bash\n$ npm install microlock\n```\n\n## Basic usage\n\n### ES5\n```javascript\nvar os = require('os');\nvar Etcd = require('node-etcd');\nvar Microlock = require('microlock');\n\nvar key = 'foo'; //name of the lock\nvar id = os.hostname(); //id of *this* node\nvar ttl = 5; //5 second lease on lock\n\nvar etcd = new Etcd();\nvar foo = new Microlock.default(etcd, key, id, ttl);\n\nfoo.lock().then(function () {\n  // foo is locked by this node\n\n  // do some stuff...\n\n  // release the lock\n  return foo.unlock();\n}, function (e) {\n  if (e instanceof Microlock.AlreadyLockedError) {\n    // foo is already locked by a different node\n  }\n});\n```\n\n### ES2015 (with babel)\n```javascript\nimport { hostname } from 'os';\nimport Etcd from 'node-etcd';\nimport Microlock, { AlreadyLockedError } from 'microlock';\n\nconst key = 'foo'; //name of the lock\nconst id = hostname(); //id of *this* node\nconst ttl = 5; //5 second lease on lock\n\nconst etcd = new Etcd();\nconst foo = new Microlock(etcd, key, id, ttl);\n\nfoo.lock().then(() =\u003e {\n  // foo is locked by this node\n\n  // do some stuff...\n\n  // release the lock\n  return foo.unlock();\n}, (e) =\u003e {\n  if (e instanceof AlreadyLockedError) {\n    // foo is already locked by a different node\n  }\n});\n```\n\n### ES2016/2017 (with babel)\n```javascript\n\nimport { hostname } from 'os';\nimport Etcd from 'node-etcd';\nimport Microlock, { AlreadyLockedError } from 'microlock';\n\nasync function main () {\n\n  const key = 'foo'; //name of the lock\n  const id = hostname(); //id of *this* node\n  const ttl = 5; //5 second lease on lock\n\n  const etcd = new Etcd();\n  const foo = new Microlock(etcd, key, id, ttl);\n\n  try {\n    await foo.lock();\n    // foo is locked by this node\n\n    // do some stuff...\n\n    // release the lock\n    await foo.unlock();\n  } catch (e) {\n    if (e instanceof AlreadyLockedError) {\n      // foo is already locked by a different node\n    }\n  }\n}\n\nmain();\n```\n\n## Methods\n\nAll methods (except destroy) return promises, making it easy to use features like async/await with ES2016/ES2017 via Babel.\n\n### Microlock(etcd, key, node_id, [ttl = 1])\nCreates a microlock client for a lock key.\n\n```javascript\nvar Etcd = require('node-etcd');\nvar Microlock = require('microlock');\n\nvar etcd = new Etcd();\nvar foo = new Microlock.default(microlock, 'foo', 'bar');\n```\n\n### .lock()\nAttempts to lock the `key` for the `node_id`.\n\n```javascript\nfoo.lock().then(function () {\n  // foo is locked by this node\n}, function (e) {\n  if (e instanceof Microlock.AlreadyLockedError) {\n    // foo is already locked by a different node\n  }\n});\n```\n\n### .unlock()\nAttempts to release the `key` for the `node_id`.\n\n```javascript\nfoo.unlock().then(function () {\n  // foo is unlocked\n}, function (e) {\n  if (e instanceof Microlock.LockNotOwnedError) {\n    // foo is not locked by `node_id`\n  }\n})\n```\n\n### .renew()\nAttempts to renew the lock on `key` for the `node_id`.\n\n```javascript\nfoo.renew().then(function () {\n  // foo lease is renewed... ttl is refreshed\n}, function (e) {\n  if (e instanceof Microlock.LockNotOwnedError) {\n    // foo is not locked by `node_id`\n  }\n})\n```\n\n### .destroy()\nUnbinds listeners/watchers from this client\n\n## Events\n\n### unlock\nEmits when the key is unlocked (node agnostic)\n\n```javascript\nfoo.on(Microlock.events.unlocked, function () {\n  //handle unlocked with constant\n});\n\nfoo.on('unlocked', function () {\n  //handle unlocked with string\n});\n```\n\n### locked\nEmits when the key is locked (node agnostic)\n\n```javascript\nfoo.on(Microlock.events.locked, function () {\n  //handle locked with constant\n});\n\nfoo.on('locked', function () {\n  //handle locked with string\n});\n```\n\n## Contributing\n\n### Installing packages\n\n```bash\n  $ make install\n```\n\n### Building\n\n```bash\n  $ make\n```\n\n### Linting\n\n```bash\n  $ make lint\n```\n\n### Running unit tests\n\n```bash\n  $ make unit\n```\n\n### Running integration tests\n\n**[Docker Compose](https://docs.docker.com/compose/) is required**\n\n```bash\n  $ make integration etcd_image_version=v2.2.2\n```\n\nYou can use whatever version you'd like to test against in the command above.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthebigredgeek%2Fmicrolock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthebigredgeek%2Fmicrolock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthebigredgeek%2Fmicrolock/lists"}