Documentation
¶
Overview ¶
Package lock provides named distributed-lock contracts with an in-memory locker; Redis and SQL backends are available via integrations.
Index ¶
- Constants
- func LockerMustFromContainer(serviceContainer containercontract.Container) lockcontract.Locker
- func LockerMustFromResolver(resolver containercontract.Resolver) lockcontract.Locker
- func NewLazyLocker(resolver containercontract.Resolver) lockcontract.Locker
- func RunExclusive(runtimeInstance runtimecontract.Runtime, locker lockcontract.Locker, ...) (bool, error)
- type ExclusiveCommand
- type InMemoryLocker
- type LeaderGate
- type LeaderGateOptions
Constants ¶
const ServiceLocker = "service.lock.locker"
Variables ¶
This section is empty.
Functions ¶
func LockerMustFromContainer ¶
func LockerMustFromContainer(serviceContainer containercontract.Container) lockcontract.Locker
func LockerMustFromResolver ¶
func LockerMustFromResolver(resolver containercontract.Resolver) lockcontract.Locker
func NewLazyLocker ¶ added in v3.11.0
func NewLazyLocker(resolver containercontract.Resolver) lockcontract.Locker
NewLazyLocker returns a Locker that resolves the registered service.lock.locker on first use; a successful resolution is reused for every later call. A failed resolution never panics: CreateLock returns a lock whose every method reports the resolution error, so LeaderGate and RunExclusive see a store outage as the acquire error they already handle, and the resolution is retried on the next CreateLock.
func RunExclusive ¶ added in v3.10.0
func RunExclusive( runtimeInstance runtimecontract.Runtime, locker lockcontract.Locker, name string, ttl time.Duration, callback func(runtimecontract.Runtime) error, ) (bool, error)
RunExclusive acquires the named lock, runs callback while holding it, and always releases afterwards, so the ttl acts only as crash-safety, never as the run cadence. It returns (false, nil) without running callback when another holder owns the lock, so N cron-launched instances run the command exactly once per tick. While callback runs, the lock is refreshed at half the ttl on a background goroutine; a failed refresh cancels the child runtime handed to callback, because the lease may now be held by another instance. A non-positive ttl selects the session-lock behavior: no lease to extend, only a liveness probe at defaultSessionProbeInterval.
Types ¶
type ExclusiveCommand ¶ added in v3.10.0
type ExclusiveCommand struct {
// contains filtered or unexported fields
}
ExclusiveCommand decorates a cli command with RunExclusive, the per-tick dedup for cron-launched commands on a multi-instance deployment: the ttl is crash-safety only (the lease is refreshed while the command runs and released as soon as it returns), so it never has to be tuned against the cron interval or the command duration.
func NewExclusiveCommand ¶ added in v3.10.0
func NewExclusiveCommand( command clicontract.Command, locker lockcontract.Locker, ttl time.Duration, ) *ExclusiveCommand
NewExclusiveCommand wraps a cli command so that, across every instance of the application, only one runs it at a time: the others skip quietly with a zero exit code. The lock name defaults to "melody:command:" plus the command name; use NewExclusiveCommandWithName when two differently-named commands must share one lock, or one command needs distinct locks per deployment.
func NewExclusiveCommandWithName ¶ added in v3.10.0
func NewExclusiveCommandWithName( command clicontract.Command, locker lockcontract.Locker, lockName string, ttl time.Duration, ) *ExclusiveCommand
func (*ExclusiveCommand) Description ¶ added in v3.10.0
func (instance *ExclusiveCommand) Description() string
func (*ExclusiveCommand) Flags ¶ added in v3.10.0
func (instance *ExclusiveCommand) Flags() []clicontract.Flag
func (*ExclusiveCommand) Name ¶ added in v3.10.0
func (instance *ExclusiveCommand) Name() string
func (*ExclusiveCommand) Run ¶ added in v3.10.0
func (instance *ExclusiveCommand) Run( runtimeInstance runtimecontract.Runtime, commandContext *clicontract.CommandContext, ) error
type InMemoryLocker ¶
type InMemoryLocker struct {
// contains filtered or unexported fields
}
func NewInMemoryLocker ¶
func NewInMemoryLocker(clockInstance clockcontract.Clock) *InMemoryLocker
func (*InMemoryLocker) CreateLock ¶
func (instance *InMemoryLocker) CreateLock(name string, ttl time.Duration) lockcontract.Lock
func (*InMemoryLocker) PurgeExpired ¶
func (instance *InMemoryLocker) PurgeExpired() int
type LeaderGate ¶ added in v3.10.0
type LeaderGate struct {
// contains filtered or unexported fields
}
LeaderGate is the become-leader, renew-periodically, release-on-shutdown pattern over any lock backend: Run campaigns for the named lock, holds and renews it while leading, demotes itself and re-campaigns when a renewal fails, and releases the lock on shutdown. Wrap the work itself in a check on IsLeader, or hook OnElected/OnLost. A non-positive ttl selects session-style locks (MySQL GET_LOCK, PostgreSQL advisory): there is no lease to extend, so the renewal is a liveness probe.
func NewLeaderGate ¶ added in v3.10.0
func NewLeaderGate(locker lockcontract.Locker, name string, ttl time.Duration) *LeaderGate
func NewLeaderGateWithOptions ¶ added in v3.10.0
func NewLeaderGateWithOptions( locker lockcontract.Locker, name string, ttl time.Duration, options LeaderGateOptions, ) *LeaderGate
func (*LeaderGate) IsLeader ¶ added in v3.10.0
func (instance *LeaderGate) IsLeader() bool
func (*LeaderGate) Run ¶ added in v3.10.0
func (instance *LeaderGate) Run(runtimeInstance runtimecontract.Runtime) error
Run blocks until the runtime context is cancelled and always returns nil on a clean shutdown; start it with `go gate.Run(runtimeInstance)` for a long-running worker. Acquire errors (a store outage) never abort it — they back off doubling, capped at defaultMaxCampaignBackoff, and campaigning resumes. Because they never abort it, they are also never returned: hook OnCampaignError to see them, or a permanent misconfiguration is indistinguishable from a deployment that simply has no work to lead.
type LeaderGateOptions ¶ added in v3.10.0
type LeaderGateOptions struct {
/* RetryInterval is the pause between failed campaigns while another instance leads; defaults to half the ttl, floored at one second. */
RetryInterval time.Duration
/* RefreshInterval is the lease-renewal cadence while leading; defaults to half the ttl, or defaultSessionProbeInterval when the ttl is non-positive (session-style locks whose Refresh is a liveness probe). */
RefreshInterval time.Duration
/* OnElected runs on the Run goroutine right after the gate becomes leader. */
OnElected func(runtimeInstance runtimecontract.Runtime)
/* OnLost runs on the Run goroutine right after leadership is lost to a failed renewal; cause is the renewal error. It does not run on a clean shutdown. */
OnLost func(runtimeInstance runtimecontract.Runtime, cause error)
/* OnCampaignError runs on the Run goroutine for every campaign that could not even ask the store who leads — the gate then backs off and campaigns again, so without this hook the error is never seen. A store outage and a permanent misconfiguration (a redis locker built with a non-positive ttl, whose Acquire fails closed on every call) are indistinguishable from the outside: both look exactly like a deployment that quietly elects no leader and does no work. */
OnCampaignError func(runtimeInstance runtimecontract.Runtime, cause error)
}
LeaderGateOptions tunes a LeaderGate; every zero value resolves to a sensible default derived from the ttl.