{"id":28407224,"url":"https://github.com/vincezk/enqueue-server","last_synced_at":"2025-06-29T13:32:42.763Z","repository":{"id":57225567,"uuid":"91226729","full_name":"VinceZK/enqueue-server","owner":"VinceZK","description":"Node Enqueue Server","archived":false,"fork":false,"pushed_at":"2017-05-16T13:10:11.000Z","size":36,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-30T08:55:13.451Z","etag":null,"topics":["concurrency","dequeue","enqueue","exclusive","lock","optimistic","share"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VinceZK.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-14T07:18:58.000Z","updated_at":"2019-01-12T03:37:36.000Z","dependencies_parsed_at":"2022-08-24T11:00:27.628Z","dependency_job_id":null,"html_url":"https://github.com/VinceZK/enqueue-server","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fenqueue-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fenqueue-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fenqueue-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fenqueue-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VinceZK","download_url":"https://codeload.github.com/VinceZK/enqueue-server/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fenqueue-server/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":258793538,"owners_count":22758983,"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":["concurrency","dequeue","enqueue","exclusive","lock","optimistic","share"],"created_at":"2025-06-02T00:37:28.426Z","updated_at":"2025-06-29T13:32:42.753Z","avatar_url":"https://github.com/VinceZK.png","language":"JavaScript","readme":"# enqueue-server\nNode enqueue server is a clean NodeJS implementation for providing locking and concurrency controls on business object level.\nIt supports 4 types of locks:\n\n1. Shared lock\n2. Exclusive Lock\n3. Exclusive but not cumulative Lock\n4. Optimistic Lock\n\n## Example\nUser A is editing a product master data, while user B also attempts to edit the same product. \nOn the UI of product mater data maintenance, B is told that the product is currently locked by A, \nand he can only display it. This is a very usual case of pessimistic lock, \nand it can be easily achieved using enqueue-server's exclusive lock.\n\n```javascript\nvar lockClient = require('enqueue-client');\nvar lock = {\"name\":\"product\",\"argument\":[\"Computer\"],\"mode\":\"E\",\"owner\":\"B\"};\n\nlockClient.lock(lock, function(lockUUID,RC,OWNER){\n   if(RC === '0'){ //Lock is acquired\n     //Allow Editing\n     lockClient.unlock(lockUUID); //Unlock the object after finish editing\n   }else{\n     //Report message: \"The product Computer is now locked by \u003cOWNER\u003e\"\n   }  \n});    \n```\n\n## Architecture and Deployment\nEnqueue server is started in one single Node process. \nIt can be either deployed in the same server with your application, or be deployed in a standalone server.\nIn both cases, it can be shared among multiple application server processes. \n\n![Enqueue Server is behind application server process](ServerAccess.png)\n\n\n## To Begin\n1. Install it:\n\n   ```bash\n   $ npm install -g enqueue-server \n   ```\n \n2. Start the enqueue server:\n\n   CD to the install path of EnqueueServer\n\n   ```bash\n   $ node index.js \n   ``` \n\n3. Test to connect:   \n\n    Download the [enqueue-client](https://www.npmjs.com/package/enqueue-client), \n    and execute following javascript in NodeJS.\n    \n    ```javascript    \n    var lockClient = require('enqueue-client');\n    lockClient.setEnqueueServerConnection('127.0.0.1', 3721);\n    \n    var lock = {\"name\":\"product\",\"argument\":[\"Computer\"],\"mode\":\"E\",\"owner\":\"B\"};\n    lockClient.lock(lock, function(lockUUID,RC,OWNER){\n       console.log('Lock is acquired with lock UUID: '+lockUUID);\n   \n    });\n    ```\n## Lock Types\n4 lock types are supported.\n\n### Shared Lock (S)\nSeveral users (transactions) can access locked data at the same time in display mode. \nRequests from further shared locks are accepted, even if they are from different users.\n\nAn exclusive lock (E) set by another user on an object that already has a shared lock will be rejected. \nEvery extended exclusive lock (X) will also be rejected.\n\n### Exclusive Locks (E)\nAn exclusive lock protects the locked object against all types of locks from other transactions. \nOnly the same lock owner can reset the lock (accumulate).\n\n### eXclusive non-cumulative (X)\nWhereas exclusive locks can be requested several times by the same transaction and released one by one, \nan exclusive, non-cumulative lock can only be requested once by the same transaction. \nEach further lock request will be rejected.\n\n### Optimistic Lock (O)\nOptimistic locks initially behave like shared locks and can be converted into exclusive locks.\n\n## Elementary Lock\nAn elementary lock contains following attributes:\n\n**name**: lock object name, usually you can use the database table name.\n\n**argument**: the key of the object, it is an array, \nas the object key may be combined with several fields. \nFor example, an object has the key combined with 3 fields.\nThe argument ['A', 'B', '@'] stands for the object with first 2 key fields equals 'A' and 'B',\nthe wildcard letter represented here by '@'.\n\n**mode**: 4 lock types: S, E, X, O.\n\n**owner**: the owner who holds the lock, it can be a login user ID.\n\n**waitTime**: time to wait until the lock is acquired, default is 0.\n\n**timeout**: the time to hold the lock, after which the elementary lock will be released automatically. \nDefault is 15 minutes.\n\n## APIs\n5 APIs are provided in the enqueue-client.\n\n### lockClient.lock(elementaryLock, callback)\nAcquire a lock for the elementaryLock. The _callback_ will be called once the result is received. \n\n```javascript    \nvar elementaryLock = {\"name\":\"product\",\"argument\":[\"Computer\"],\"mode\":\"E\",\"owner\":\"B\"};\nlockClient.lock(elementaryLock, function(lockUUID,RC,MSG){\n   //lockUUID is a unique id for the acquired lock, you must record it so that you can unlock it afterward;\n   //RC stands for the return code. 0:Success, 1:Fail, 3:Error in client, 4:Error in server;\n   //MSG returns the detail error message if RC is 3 or 4, and the lock owner if RC is 1;      \n});\n```\n\n### lockClient.unlock(lockUUID [,callback])\nRelease the lock with the specified lock UUID. The _callback_ is optional. \n\n```javascript\nlockClient.unlock(lockUUID, function(RC,MSG){\n   //RC: 0:Success, 4:Error in server;\n   //MSG returns the detail error message if RC is 4;      \n});\n```\n    \n### lockClient.promote(lockUUID, callback)\nPromote the optimistic lock with the specified lock UUID. The _callback_ receives the response from server. \n\n```javascript\nlockClient.promote(lockUUID, function(RC,MSG){\n   //RC: 0:Success, 2:Fail, 3:Error in client, 4:Error in server;\n   //MSG returns the detail error message if RC is 3 or 4, and the existing lock owner if RC is 2;      \n});\n```\n \n### lockClient.getLocksBy(lockName, lockOwner, callback)\nGet a list of existing locks in the enqueue server by lockName and lockOwner. \nThe callback receives an array of locks. \nIf you give both lockName and lockOwner, it filters with both. \nIf you only give either lockName or lockOwner, it filters with one of the given.\nIf you assign null for both, it return the complete lock list. \n\n```javascript\nlockClient.getLocksBy(null, null, function(RC,locks){\n   //RC: 0:Success, 3:Error in client;\n   //locks is an array contains the locks if RC is 0 , and an error message if RC is 2;      \n});\n```\n    \n### lockClient.setEnqueueServerConnection(host, port)\nSet the remote enqueue server's host and port. \nThe method is optional if the enqueue server lies on the same server as the lock client. \nAs the default host and port are \"127.0.0.1\" and \"3721\".\n    \n## Tests\nYou can find all the unit tests in the _test_ folder, and run them by:\n\n   ```bash\n   $ npm run test \n   ``` \n\n## Performance\nPerformance is tested to support around 3000 lock/unlock requests per second.\n\n## License\n[The MIT License](http://opensource.org/licenses/MIT)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvincezk%2Fenqueue-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvincezk%2Fenqueue-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvincezk%2Fenqueue-server/lists"}