Locksmith provides a simple way to obtain shared locks between applications.
This project provides:
- server implementation (binary + docker image)
- a command line utility
locksmithctl - a sample client
- client library packages (see
/pkg)
Documentation provided inline.
The locksmith server can be installed in two different ways. To get the server container image, run:
docker pull ghcr.io/maansaake/locksmith:latestYou can browse available versions here: https://github.com/maansaake/locksmith/pkgs/container/locksmith
Run go install to instead get the server binary, make sure you have set either GOPATH or GOBIN.
go install github.com/maansaake/locksmith@latestEither...
docker run ghcr.io/maansaake/locksmith:latestOr...
locksmithBoth the binary and the docker container have configuration options that are consumed via environment variables. All variables are namespaced and prefixed with LOCKSMITH_ to avoid collisions.
LOCKSMITH_LOG_VERBOSITY: Sets the verbosity level for logging. Higher values produce more verbose output (default:0)LOCKSMITH_LOG_OUTPUT: Sets the log format. Must be eitherjsonorconsole(default:console)LOCKSMITH_PORT: The port where the locksmith server is reachable (default:9000)LOCKSMITH_TLS: If set totrue, TLS is enabled for the locksmith server (default:false). When enabled, bothLOCKSMITH_TLS_CERT_PATHandLOCKSMITH_TLS_KEY_PATHmust be providedLOCKSMITH_TLS_CERT_PATH: Absolute path to the server's certificate (default:/etc/cert/locksmith.pem)LOCKSMITH_TLS_KEY_PATH: Absolute path to the server's private key (default:/etc/cert/locksmith.key)LOCKSMITH_TLS_REQUIRE_CLIENT_CERT: When set totrue(default:false), client connections will have their certificates validated against the client CA certificate. You must provideLOCKSMITH_TLS_CLIENT_CA_CERT_PATHwhen this variable is setLOCKSMITH_TLS_CLIENT_CA_CERT_PATH: Absolute path to the client CA certificate (default:/etc/cert/client_ca.cert)LOCKSMITH_OBSERVABILITY: Set totrueto enable OpenTelemetry instrumentation (default:false)LOCKSMITH_RUNTIME_METRICS: Set tofalseto disable Go runtime metrics. Only applicable whenLOCKSMITH_OBSERVABILITYistrue(default:true)
These options are available to enable optimizations for systems where locksmith is expected to handle very high loads. These parameters are not recommended to be changed at all for most use cases as the defaults are meant to be good enough for 99% of cases. Ensure that any changes to these values are preceded by rigorous load testing in the intended environment.
LOCKSMITH_Q_TYPE: Testing utility, there are only two options:singleandmulti(default:multi). Thesingleoption completely removes concurrency, making locksmith inefficient at handling higher throughput since it congests lock access to one go-routine, but making it easier to test in some situationsLOCKSMITH_Q_CONCURRENCY: Only applicable formultitype queueing, sets the number of go-routines serving incoming requests (default:10)LOCKSMITH_Q_CAPACITY: Only applicable formultitype queueing, sets the size of each serving go-routines work queue (default:100)
The command line utility must be installed via go install.
go install github.com/maansaake/locksmith/cmd/locksmithctl@latestUser guidance is provided as part of the CLI's UX:
╭──────────────────────────────────────╮
│ 🔑 locksmithctl │
│ Interactive Locksmith Lock Manager │
│ Connecting to localhost:9000 │
╰──────────────────────────────────────╯
✓ Connected to localhost:9000
Commands
acquire [lock] Acquire a lock (interactive prompt if omitted)
release [lock] Release a lock (picker if omitted)
list Show all acquired locks
reconnect Reconnect to the server
help Show this help
exit / quit Exit locksmithctl
locksmith> acquire locktag
→ Acquire request sent for locktag (waiting for server acknowledgement…)
✓ Acquired: locktag
locksmith> list
╭──────────────────╮
│ Active Locks (1) │
│ 1. locktag │
╰──────────────────╯
Import and use the client in your own Go-code:
package main
import (
"fmt"
"github.com/maansaake/locksmith/pkg/client"
)
func main() {
acquiredFunc := func(lockTag string) {
fmt.Println("acquired lock tag: " + lockTag)
}
locksmithClient := client.New(&client.Opts{
Host: "localhost",
Port: 9000,
OnAcquired: acquiredFunc,
})
if err := locksmithClient.Connect(); err != nil {
panic("uh oh, client failed to connect :-(")
}
if err := locksmithClient.Acquire("some-lock-tag"); err != nil {
panic("failed to acquire")
}
// await call to acquiredFunc
}Or use the protocol package directly to write your own client. See the ClientMessage and ServerMessage types and the interface functions used for encoding/decoding.
Locksmith uses OpenTelemetry for instrumentation. When LOCKSMITH_OBSERVABILITY is set to true, the OTEL SDK is initialized and the autoexport package is used to select the metrics exporter based on standard OTEL environment variables. By default the OTLP exporter is used; set OTEL_METRICS_EXPORTER to change this (e.g. prometheus, console, or none). See the OTEL SDK environment variable specification and the OTLP exporter configuration reference for the full list of supported variables.
Locksmith registers the following OTEL instruments:
| Instrument | Type | Description |
|---|---|---|
locksmith.locks |
UpDownCounter | Number of currently held locks |
locksmith.acquires |
Counter | Total successful lock acquires since start |
locksmith.releases |
Counter | Total successful lock releases since start |
locksmith.rejections |
Counter | Rejections due to client misbehavior; carries a reason attribute (bad_manners, unnecessary_acquire, or unnecessary_release) |
When LOCKSMITH_RUNTIME_METRICS is true (the default), the following Go runtime instruments from the OTEL Go runtime semconv are also recorded:
| Instrument | Unit | Description |
|---|---|---|
go.memory.used |
By | Memory used by the Go runtime |
go.memory.limit |
By | Go runtime memory limit, if configured |
go.memory.allocated |
By | Memory allocated to the heap |
go.memory.allocations |
{allocation} | Count of heap allocations |
go.memory.gc.goal |
By | Heap size target at end of GC cycle |
go.goroutine.count |
{goroutine} | Number of live goroutines |
go.processor.limit |
{thread} | OS threads available for user-level Go code |
go.config.gogc |
% | Heap size target percentage (GOGC) |
Note: the instrument names above are the OTEL API names. The exact metric names seen in your monitoring system depend on the exporter. For example, the Prometheus exporter converts dots to underscores and appends
_totalto counters (locksmith.acquires→locksmith_acquires_total), while the OTLP exporter preserves the dot notation.