{"id":19826246,"url":"https://github.com/aternosorg/php-lock","last_synced_at":"2025-05-01T14:31:02.603Z","repository":{"id":34911839,"uuid":"189358647","full_name":"aternosorg/php-lock","owner":"aternosorg","description":"Distributed exclusive and shared resource locking based on etcd","archived":false,"fork":false,"pushed_at":"2025-04-08T15:22:48.000Z","size":131,"stargazers_count":5,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-30T21:46:42.310Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://packagist.org/packages/aternos/lock","language":"PHP","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/aternosorg.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}},"created_at":"2019-05-30T06:30:16.000Z","updated_at":"2025-04-08T15:06:06.000Z","dependencies_parsed_at":"2023-02-13T20:45:50.217Z","dependency_job_id":null,"html_url":"https://github.com/aternosorg/php-lock","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aternosorg%2Fphp-lock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aternosorg%2Fphp-lock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aternosorg%2Fphp-lock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aternosorg%2Fphp-lock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aternosorg","download_url":"https://codeload.github.com/aternosorg/php-lock/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251889965,"owners_count":21660422,"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-11-12T11:09:51.980Z","updated_at":"2025-05-01T14:31:02.352Z","avatar_url":"https://github.com/aternosorg.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PHP library for distributed locking\nDistributed exclusive and shared resource locking based on [etcd](https://github.com/etcd-io/etcd) (using [aternos/etcd](https://github.com/aternosorg/php-etcd)).\n\n### About\nThis library was created to provide a highly available distributed locking solution for a file storage system,\nwhere a failed or incorrect lock could lead to potential data loss or corruption. Therefore it tries to achieve\nthe best possible consistency while still providing simple usability, stability and scalability. There are lots \nof other locking libraries out there. Some of them don't allow shared locks, some of them implement complicated \nalgorithms on the client-side which also didn't seem to fit our needs. Therefore we've decided to create our own solution \nand let etcd do most of the heavy lifting.\n\nEtcd is a fast and reliable key-value store that allows transaction operations to ensure that no unexpected changes were made \nsince the last read (see `putIf()` and `deleteIf()` in [`Aternos\\Etcd\\Client`](https://github.com/aternosorg/php-etcd/blob/master/src/Client.php)).\nThis locking library uses those methods to ensure consistency. If two processes try to lock the same resource at the same\ntime one of them will be denied by etcd and then retry its lock by either adding it to the other lock (shared) or waiting\nfor the other lock to finish (exclusive). If more processes are fighting over the same lock, they will start to delay their\nretries in random intervals to find a consistent conclusion.\n\nTimeouts (UnavailableExceptions) from etcd will also be detected and the operations retried after some delay to avoid\nproblems because of short availability problems.\n\nThis library was extracted from a different project to be used in different places and not only for a file storage.\nTherefore we've already used this in production to create thousands of locks every minute in only a few milliseconds.\n\n### Installation\nThe gRPC PHP extension has to be installed to use this library. See [aternos/etcd](https://github.com/aternosorg/php-etcd#installation).\n\n```bash\ncomposer require aternos/lock\n```\n\n## Usage\nThe most important class is the [`Lock`](src/Lock.php) class. Each instance of this class represents\none lock on a resource that is identified by a key. There are also several static functions to set options\nfor all locks, but all of them have default values, so you can just start by creating a lock:\n\n```php\n\u003c?php\n\n$lock = new \\Aternos\\Lock\\Lock(\"key\");\n\n// try to acquire lock\nif($lock-\u003elock()) {\n    // do something\n} else {\n    // error/exit\n}\n```\n\n### Exclusive/shared locks\nThere can only be one exclusive lock at the same time, but there can be multiple shared locks. If there is\nany shared lock an exclusive lock is not possible. Exclusive locks can be useful for write operations while\nshared locks can be useful for read operations. By default all locks are shared, you can create an exclusive\nlock like this:\n\n```php\n\u003c?php \n\n$lock-\u003elock(true);\n```\n\n### Locking time(out) and refreshing\nYou always have to set a timeout for your locks. The lock will be released automatically if the time runs out.\nThis is necessary to avoid infinite dead locks. The timeout can be refreshed e.g. after writing or reading a\nchunk of data. You can also define a threshold for the refresh time. The lock will only be refreshed if\nthe remaining locking time is below this threshold. This avoids spamming etcd with unnecessary queries.\n\n```php\n\u003c?php \n\n$lock-\u003elock(true, 60); // 60 seconds timeout\n\n// refresh the lock (with default values)\n$lock-\u003erefresh();\n\n// refresh with 120 seconds timeout\n$lock-\u003erefresh(120);\n\n// refresh with 120 seconds timeout, but only if remaining time is lower than 60 seconds\n$lock-\u003erefresh(120, 60);\n```\n\n### Waiting for other locks\nIf the resource that you want to access is currently locked exclusively or you need an exclusive lock and there\nare still shared locks, you might want to wait some time for the locks to be released. You can specify a maximum\ntime to wait for other locks:\n\n```php\n\u003c?php \n\n$lock-\u003elock(true, 60, 300); // wait 300 seconds for other locks\n\n// check if the lock was actually acquired or if the wait timeout was reached\nif($lock-\u003eisLocked()) {\n    // do something\n} else {\n    // error/exit\n}\n```\n\n### Break the lock\nOf course it's important to break the lock after finishing the operation to avoid running into timeouts. You can break\na lock like this:\n\n```php\n\u003c?php\n\n$lock = new \\Aternos\\Lock\\Lock(\"key\");\n\n// check if the lock was successful\nif($lock-\u003eisLocked()) {\n    // do something\n    $lock-\u003ebreak();\n} else {\n    // error/exit\n}\n```\n\n### Identifier\nYou can identify yourself (the current process/request) by providing and identifier string. By default this library\nuses the `uniqid()` function to create a unique identifier. There aren't many reasons to provide a custom identifier.\nThe default identifier is the same for all locks in the current process. Therefore all those locks are able to take\nover the other locks that were previously created in the same process. It also would be possible to see which other\nprocess (represented by its identifier) is currently holding a lock. There is currently no such function, but that \nwould be easy to add.\n\nThere are two ways to provide a custom identifier. Either by setting the default for all locks or by overwriting \nthe default identifier on a specific lock:\n\n```php\n\u003c?php\n\n\\Aternos\\Lock\\Lock::setDefaultIdentifier(\"default-identifier\");\n\n// uses the previously set \"default-identifier\"\n$lock-\u003elock();\n\n// overwrites the \"default-identifier\" with \"different-identifier\"\n$lock-\u003elock(true, 60, 300, \"different-identifier\");\n```\n\n### Settings\nThe default identifier above is already one of the static settings, which you can specify for all locks. All settings \nare stored in protected static fields in the [`Lock`](src/Lock.php) class (at the top) and have their own static\nsetter functions. Below are only a few important ones, but you can read the PHPDoc function comments for further explanations\nof the other settings. Only change those if you know what you are doing.\n\n#### Etcd client\nYou can set an etcd client object to specify the etcd connection details. See [this](https://github.com/aternosorg/php-etcd#client-class)\nfor more information.\n\n```php\n\u003c?php\n\n$client = new Aternos\\Etcd\\Client();\n$client = new Aternos\\Etcd\\Client(\"localhost:2379\");\n$client = new Aternos\\Etcd\\Client(\"localhost:2379\", \"username\", \"password\");\n\n\\Aternos\\Lock\\Lock::setClient($client);\n```\n\n#### Etcd key prefix\nYou can set a prefix for all locking keys in etcd to avoid conflicts with other data in the same etcd cluster.\nThe default prefix is `lock/`.\n\n```php\n\u003c?php\n\n\\Aternos\\Lock\\Lock::setPrefix(\"my-prefix/\");\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faternosorg%2Fphp-lock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faternosorg%2Fphp-lock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faternosorg%2Fphp-lock/lists"}