{"id":16847214,"url":"https://github.com/graup/es-distributed-lock","last_synced_at":"2025-03-18T07:16:41.389Z","repository":{"id":57510493,"uuid":"237937640","full_name":"graup/es-distributed-lock","owner":"graup","description":"Distributed Lock using Elasticsearch","archived":false,"fork":false,"pushed_at":"2020-02-13T09:53:53.000Z","size":33,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-24T13:44:32.463Z","etag":null,"topics":["distributed-systems","golang","lock"],"latest_commit_sha":null,"homepage":"","language":"Go","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/graup.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":"2020-02-03T10:17:35.000Z","updated_at":"2022-11-23T06:48:30.000Z","dependencies_parsed_at":"2022-09-26T17:50:56.270Z","dependency_job_id":null,"html_url":"https://github.com/graup/es-distributed-lock","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/graup%2Fes-distributed-lock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graup%2Fes-distributed-lock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graup%2Fes-distributed-lock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/graup%2Fes-distributed-lock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/graup","download_url":"https://codeload.github.com/graup/es-distributed-lock/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244173553,"owners_count":20410300,"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-systems","golang","lock"],"created_at":"2024-10-13T13:07:06.835Z","updated_at":"2025-03-18T07:16:41.363Z","avatar_url":"https://github.com/graup.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"Distributed Lock using Elasticsearch\n====================================\n\n[![Travis_ci](https://travis-ci.org/graup/es-distributed-lock.svg?branch=master)](https://travis-ci.org/graup/es-distributed-lock)\n[![codecov](https://codecov.io/gh/graup/es-distributed-lock/branch/master/graph/badge.svg)](https://codecov.io/gh/graup/es-distributed-lock)\n\nThis go module implements a primitive distributed lock using Elasticsearch.\nThis is useful if you're already using ES anyway and need an easy way to add locks to improve efficiency.\nFor example, when you have two concurrent processes writing to a shared ES storage but want to avoid both\nperforming the same work. This can be used to distribute workload (processes take turns)\nor for failover operation (one process does all the work, but in case it dies, another process is ready to take over).\n\nHow it works\n------------\n\nTo acquire a lock, this creates a document in an ES index with an owner and expiry time.\nIf such a document already exists, the following rules apply:\n- If the current owner is the same, set the new expiry time (this way, clients can extend their currently held locks' TTL).\n- If the owner is different and the lock is expired, override owner and expiry time.\n- If the owner is different and the lock is not expired, the operation is rejected.\n\nLocks can be manually released, which deletes the document in the index if the owner still matches (this avoid errorneously deleting other client's locks).\nIf a lock is not released, it can be taken over by another client after it expires.\n\nNote that this has no correctness guarantees: it is still possible (although unlikely) that more than one process acquires a lock.\nHowever, Elasticsearch itself has consistency guarantees, so you should use\n[Optimistic Concurrency Control](https://qbox.io/blog/optimistic-concurrency-control-in-elasticsearch) on the storage layer to solve data conflicts.\n\n**Which TTL should I use?**\n\nThat depends on your availability requirements. I suggest times of 15-30 seconds.\nAvoid extremely short TTLs (less than 5 seconds) as this may create timing issues. \n\nAPI\n---\n\n```go\npackage lock // import \"github.com/graup/es-distributed-lock\"\n\ntype Lock struct {\n\tID       string    `json:\"-\"`\n\tOwner    string    `json:\"owner\"`\n\tAcquired time.Time `json:\"acquired\"`\n\tExpires  time.Time `json:\"expires\"`\n}\n    Lock implements a distributed lock using Elasticsearch. The use case of this\n    lock is improving efficiency (not correctness)\n\nfunc NewLock(client *elastic.Client, id string) *Lock\n    NewLock create a new lock identified by a string\n\nfunc (lock *Lock) Acquire(ctx context.Context, ttl time.Duration) error\n    Acquire tries to acquire a lock with a TTL. Returns nil when succesful or\n    error otherwise.\n\nfunc (lock *Lock) IsAcquired() bool\n    IsAcquired returns if lock is acquired and not expired\n\nfunc (lock *Lock) IsReleased() bool\n    IsReleased returns if lock was released manually or is expired\n\nfunc (lock *Lock) KeepAlive(ctx context.Context, beforeExpiry time.Duration) error\n    KeepAlive causes the lock to automatically extend its TTL to avoid\n    expiration. This keep going until the context is cancelled, Release() is\n    called, or the process dies. This calls Acquire again {beforeExpiry} before\n    expirt. Don't use KeepAlive with very short TTLs, rather call Acquire\n    yourself when you need to.\n\nfunc (lock *Lock) MustRelease() error\n    MustRelease removes the lock (if it is still held) but returns an error if\n    the result was a noop.\n\nfunc (lock *Lock) Release() error\n    Release removes the lock (if it is still held). The only case this errors is\n    if there's a connection error with ES.\n\nfunc (lock *Lock) WithOwner(owner string) *Lock\n    WithOwner is a shortcut method to set the owner manually. If you don't\n    specify an owner, a random UUID is used automatically.\n```\n\nReferences\n----------\n\n- [Optimistic Concurrency Control in Elasticsearch. Adam Vanderbush (2017)](https://qbox.io/blog/optimistic-concurrency-control-in-elasticsearch)\n- [How to do distributed locking. Martin Kleppmann (2016)](https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html)\n- [ElasticSearch.DistributedLock implementation in C# (2015)](https://github.com/dmombour/ElasticSearch.DistributedLock)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraup%2Fes-distributed-lock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgraup%2Fes-distributed-lock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgraup%2Fes-distributed-lock/lists"}