{"id":16642132,"url":"https://github.com/artskydj/level-lock","last_synced_at":"2025-10-19T03:53:29.612Z","repository":{"id":22531980,"uuid":"25872625","full_name":"ArtskydJ/level-lock","owner":"ArtskydJ","description":"in-memory advisory read/write locks for leveldb keys","archived":false,"fork":false,"pushed_at":"2014-10-28T16:20:46.000Z","size":87,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-18T15:53:41.955Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"substack/level-lock","license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ArtskydJ.png","metadata":{"files":{"readme":"readme.markdown","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":"2014-10-28T14:14:37.000Z","updated_at":"2022-09-17T20:06:00.000Z","dependencies_parsed_at":"2022-11-02T04:16:17.613Z","dependency_job_id":null,"html_url":"https://github.com/ArtskydJ/level-lock","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArtskydJ%2Flevel-lock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArtskydJ%2Flevel-lock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArtskydJ%2Flevel-lock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArtskydJ%2Flevel-lock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ArtskydJ","download_url":"https://codeload.github.com/ArtskydJ/level-lock/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243165357,"owners_count":20246721,"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-12T07:48:57.190Z","updated_at":"2025-10-19T03:53:24.589Z","avatar_url":"https://github.com/ArtskydJ.png","language":"JavaScript","readme":"# level-lock\n\nin-memory advisory read/write locks for leveldb keys\n\n[![build status](https://secure.travis-ci.org/substack/level-lock.png)](http://travis-ci.org/substack/level-lock)\n\n# example\n\nA very common use-case for locking is to prevent race conditions when checking\nto see if a username has been taken.\n\nIn a naive solution, a `get()` followed by a `put()` runs the risk that 2\nrequests might come in at roughly the same time and that both calls to `get()`\ncould finish before either call to `put()`, resulting in 2 calls to `put()` and\nleaving the database in an inconsistent state.\n\nHowever, if we obtain a write lock on a key before checking for the existence of\nthat key with `get()` and then only release the lock after the `put()` has\ncompleted, the sequence of operations can be safely performed.\n\nHere is an example:\n\n``` js\nvar level = require('level');\nvar db = level('/tmp/users.db', { valueEncoding: 'json' });\nvar lock = require('level-lock');\n\nvar username = process.argv[2];\nvar key = 'users!' + username;\nvar userdata = { bio: 'beep boop' };\n\nvar unlock = lock(db, key, 'w');\nif (!unlock) return exit(1, 'locked');\n \ndb.get(key, function (err, value) {\n    if (value) {\n        unlock();\n        return exit(1, 'that username already exists');\n    }\n    \n    db.put('users!substack', userdata, function (err) {\n        unlock();\n        if (err) return exit(1, err);\n        console.log('created user ' + username);\n    });\n});\n\nfunction exit (code, err) {\n    console.error(err);\n    process.exit(code);\n}\n```\n\nTo drive the point further home, here is code that concretely demonstrates the\nproblem:\n\n``` js\nvar level = require('level');\nvar db = level('/tmp/race.db', { valueEncoding: 'json' });\nvar lock = require('level-lock');\n\nvar name = process.argv[2];\nvar data = { bio: 'beep boop' };\n\nfor (var i = 0; i \u003c 3; i++) (function (i) {\n    create(name, data, function (err) {\n        console.error(i + ' create: ' + (err || 'ok'));\n    });\n})(i);\n\nfunction create (name, data, cb) {\n    var key = 'users!' + name;\n    \n    //var unlock = lock(db, key, 'w');\n    //if (!unlock) return cb(new Error('locked'));\n    \n    db.get(key, function (err, value) {\n        if (value) return cb(new Error('that username already exists'));\n        \n        db.put('users!substack', data, function (err) {\n            //unlock();\n            cb(err);\n        });\n    });\n}\n```\n\nIf we run this program, then the user substack is created 3 separate times,\nsubverting our check to see if a username already exists:\n\n```\n$ node race.js substack\n0 create: ok\n1 create: ok\n2 create: ok\n```\n\nHowever, if the locking code is un-commented, then a user is only created once:\n\n```\n$ node race.js substack\n1 create: Error: locked\n2 create: Error: locked\n0 create: ok\n```\n\nNote however that just like the unix system call `flock(2)`, these locks are\nmerely advisory so code that does not check for locks can still cause\nconsistency problems.\n\n# methods\n\n``` js\nvar lock = require('level-lock')\n```\n\n## lock(db, key, mode='w')\n\nCreate a lock on a `key` with a `mode`.\n\n`mode` can be `'r'`, `'w'`, or `'rw'`.\n\nThe `keyEncoding` of `db` will be respected when setting a lock on a key.\n\nLocks are stored in-memory on the `db` object under `db._locks`.\n\n# install\n\nWith [npm](https://npmjs.org) do:\n\n```\nnpm install level-lock\n```\n\n# license\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartskydj%2Flevel-lock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fartskydj%2Flevel-lock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartskydj%2Flevel-lock/lists"}