https://github.com/freckle/aws-s3-lock-action
https://github.com/freckle/aws-s3-lock-action
ghvm-managed
Last synced: 20 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/freckle/aws-s3-lock-action
- Owner: freckle
- License: mit
- Created: 2023-11-01T12:27:59.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2026-05-23T13:27:13.000Z (27 days ago)
- Last Synced: 2026-05-23T15:20:40.701Z (27 days ago)
- Topics: ghvm-managed
- Language: TypeScript
- Homepage:
- Size: 3.42 MB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
# AWS S3 Lock
Wait for, acquire, and release a distributed lock via S3 storage.
## Usage
```yaml
- uses: aws-actions/configure-aws-credentials@v4
with:
# ...
# This blocks until the lock is acquired, or errors if timeout is reached
- uses: freckle/aws-s3-lock-action@v1
with:
# Required
bucket: an-existing-s3-bucket
# Optional, defaults shown
# name: {workflow}/{job}
# expires: 15m
# timeout: {matches expires}
# timeout-poll: 5s
# context: "{workflow} #{run}"
- run: echo "Lock held, do work here"
```

The lock is released (the S3 object deleted) in our Post step, which provides a
pretty robust guarantee of release. Expired locks are ignored (not deleted), so
it's recommended you put a Lifecyle policy on the Bucket to clean them up after
some time.
## Usage (Multi-Job)
```yaml
jobs:
acquire-lock:
# ...
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
# ...
# This blocks until the lock is acquired, or errors if timeout is reached
- id: lock
uses: freckle/aws-s3-lock-action/acquire@v1
with:
# Required
bucket: an-existing-s3-bucket
# Optional, defaults shown
# name: {workflow}/{job}
# expires: 15m
# timeout: {matches expires}
# timeout-poll: 5s
# context: "{workflow} #{run}"
outputs:
key: ${{ steps.lock.outputs.key }}
work:
# ...
needs:
- acquire-lock
steps:
- run: echo "Lock held, do work here"
release:
# ...
if: ${{ always() }}
needs:
- acquire-lock
- work
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
# ...
- uses: freckle/aws-s3-lock-action/release@v1
with:
bucket: an-existing-s3-bucket
key: ${{ needs.acquire-lock.outputs.key }}
```
## Inputs
| name | description | required | default |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------- |
| `bucket` |
Name of an existing S3 bucket to use.
| `true` | `""` |
| `name` | Name for the lock object. Include any prefix you want within the bucket. The key will be built as "name.created.uuid.expires".
| `false` | `${{ github.workflow }}/${{ github.job }}` |
| `expires` | How long before concurrent operations consider this lock expired.
| `false` | `15m` |
| `timeout` | How long to wait for the lock to become available. Default matches expires.
| `false` | `""` |
| `timeout-poll` | How long to wait between attempts for the lock. Default is 5s.
| `false` | `5s` |
| `context` | Additional context to write as the body of the lock file. Concurrent operations waiting on this lock will display it.
| `false` | `${{ github.workflow }} #${{ github.run_number }}` |
## Outputs
| name | description |
| ------------- | ------------------------------------------------- |
| `key` |
Key of the S3 object representing the lock
|
| `acquired-at` | Timestamp the lock was acquired
|
## Implementation Details
### Algorithm
This tool implements a version of the locking algorithm described in this
[StackOverflow answer][answer].
[answer]: https://stackoverflow.com/questions/45222819/can-pseudo-lock-objects-be-used-in-the-amazon-s3-api/75347123#75347123
- Upload a lock object to S3 at `...`
All time values are milliseconds since epoch.
- List all other lock objects (prefix `.`)
Filter out any expired keys (looking at `expires`) and sort, which implicitly
means by `created` then `uuid` as desired.
- If the first one (i.e. oldest) is our own, we've acquired the lock
- If not, we lost the race; remove our object, wait, and try again
### Ordering Semantics
Each time we attempt to acquire the lock, we create a new key name (e.g.
`` and `` both change), this effectively loses our "place in
line" but ensures that expiry is measured from time of acquisition and not time
of first attempt. There are trade-offs either way, and possible room for
improvement, so this is just how we're doing it for now.
### Caveat
**This tool is not meant to be bullet-proof**. We built it for our needs and
accept that there are simply no strong guarantees in this locking mechanism's
operation at scale. Your mileage may vary; patches welcome.
## Versioning
Versioned tags will exist, such as `v1.0.0` and `v2.1.1`. Branches will exist
for each major version, such as `v1` or `v2` and contain the newest version in
that series.
## Release
To trigger a release (and update the `@v{major}` tag), merge a commit to `main`
that follows [Conventional Commits][]. In short,
- `fix:` to trigger a patch release,
- `feat:` for minor, and
- `feat!:` and major
We don't enforce conventional commits generally (though you are free do so),
it's only required if you want to trigger release.
[conventional commits]: https://www.conventionalcommits.org/en/v1.0.0/#summary
---
[LICENSE](./LICENSE)