waymaker-client/proto/waymaker_locks.proto

277 lines
15 KiB
Protocol Buffer

syntax = "proto3";
package waymaker;
// Specifies the Go package for the generated code.
option go_package = "/apis/waymaker";
// VERSION COMPATIBILITY POLICY
// ----------------------------
// This proto is the wire contract for the WaymakerService gRPC API.
// To preserve backwards compatibility across rolling upgrades:
//
// 1. Existing field numbers MUST NOT be reused. To remove a field,
// add a `reserved <number>;` line instead.
// 2. Existing field types MUST NOT change.
// 3. New fields MUST be `optional` (proto3 default) and SHOULD have
// sensible zero-value semantics so old clients setting nothing
// and old servers ignoring the field both stay correct.
// 4. New enum values MUST be appended at the end. Never renumber.
// 5. The package name and service name MUST NOT change.
// 6. RPC removal is a major break — bump to a new package
// (e.g. `waymaker.v2`) instead.
//
// Reserved field numbers below are tombstones for fields that were
// considered but not yet shipped; do not reuse them.
// WaymakerService defines a gRPC service for managing distributed locks.
service WaymakerService {
// Lock attempts to acquire a lock based on the provided LockRequest.
// The response is a stream of LockEvent messages that provide updates
// on the status of the lock acquisition.
rpc Lock (LockRequest) returns (stream LockEvent) {}
// ReadLock attempts to acquire a read lock, which allows multiple readers
// but no writers to hold the lock simultaneously. The response is a stream
// of LockEvent messages that provide updates on the status of the lock acquisition.
rpc ReadLock (LockRequest) returns (stream LockEvent) {}
// UnLock releases a previously acquired lock based on the provided UnLockRequest.
// The response is an UnLockResponse indicating the success or failure of the operation.
rpc UnLock (UnLockRequest) returns (UnLockResponse) {}
// LeaseStatus retrieves the current status of a lock lease based on the provided
// LeaseStatusRequest. The response is a LeaseStatusResponse containing details
// about the lease.
rpc LeaseStatus (LeaseStatusRequest) returns (LeaseStatusResponse) {}
// ExtendLease extends the lease of an already acquired lock based on the provided
// ExtendLeaseRequest. The response is an ExtendLeaseResponse indicating the success
// or failure of the operation and the updated lease information.
rpc ExtendLease (ExtendLeaseRequest) returns (ExtendLeaseResponse) {}
// MultiLock acquires N locks atomically — all keys are granted or none are.
// The server sorts keys lexicographically to guarantee deadlock-free
// ordering between any two MultiLock callers (without this, callers asking
// for (k1,k2) and (k2,k1) could deadlock under contention). `max_wait_period`
// applies to the whole batch as a single deadline, not per key. On any
// failure the server releases every key it already acquired in this batch
// before returning. Returns a unary response — no streaming.
rpc MultiLock (MultiLockRequest) returns (MultiLockResponse) {}
// ListAcquiredLocks returns every lock CURRENTLY HELD on the node serving the
// request (the node that owns each key via the consistent-hash ring). It is a
// read-only operator/introspection surface (waymaker-ctl `locks list`); it does
// NOT include waiters. An optional `key_prefix` filters the result server-side.
// In a multi-node cluster, call it on each node to see the full picture, since
// each node only holds the locks for the keys it owns.
rpc ListAcquiredLocks (ListAcquiredLocksRequest) returns (ListAcquiredLocksResponse) {}
}
// LockEventType defines the various types of events that can occur during
// the lock acquisition process.
enum LockEventType {
Unknown = 0; // Default value when the event type is not known.
Waiting = 1; // Indicates that the lock request is waiting to be granted.
Acquired = 2; // Indicates that the lock has been successfully acquired.
Failed = 3; // Indicates that the lock request has failed.
Expired = 4; // Indicates that the lock has expired.
Heartbeat = 5; // Periodic event indicating that the lock is still active.
}
// FenceScope controls ONE thing: how durable the per-key fence_token
// counter (the monotonic uint64) is across failures. Three things it does
// NOT control — do not conflate them with the scope:
//
// * Whether a HELD LOCK survives a node loss / rollout. That is
// cluster.replication-factor plus secondary adoption, and it already
// applies to every non-Ephemeral lock regardless of scope. A stronger
// scope does not make a lock survive; a replicated lease does.
// * Client-side transparency across a primary bounce. The lock client
// transparently re-binds its event stream and re-confirms ownership
// after a disconnect, but that is a client-lib behaviour — no scope
// value changes it.
// * Mutual exclusion. Holding the lock is NOT, by itself, a guarantee
// that no one else acts. The holder MUST validate fence_token at its
// own side effect (the DB write / object PUT) and reject anything
// carrying a fence below the last one it durably committed. Even
// ScopeQuorum does not let you skip that check — an all-at-once
// cluster restart can still drop an in-memory token. See USAGE.md
// "Fence tokens" for the enforcement rule.
//
// Unspecified: server treats as Ephemeral.
// Ephemeral: per-key counter in RAM on the owning node. Resets on
// process restart or hash-ring rebalance. Fast — no I/O.
// Right for rate limiting, cache lockout, advisory locks,
// anywhere fence resets across failure are tolerable.
// Local: per-key counter persisted to disk on the owning node.
// Survives process restart on the same node. Still resets
// on hash-ring rebalance (a different node has its own
// disk). One fsync per acquire (~1-5ms on SSD).
// Quorum: Raft-replicated per-key counter — cluster-wide monotonic,
// survives any single-node failure (the surviving quorum
// keeps the count). One Raft commit per acquire (~5ms).
// Requires the cluster Raft backend to be wired; a
// single-node or test build returns BadInput for this
// scope. Pick this when an external resource fences on the
// token and two holders must never see fences that fail to
// prove an ordering. (Named Quorum, not Global: the
// guarantee is "a Raft quorum agrees on the count", which
// carries its own limit and makes no geographic claim.)
enum FenceScope {
ScopeUnspecified = 0;
ScopeEphemeral = 1;
ScopeLocal = 2;
// Wire value 3 is unchanged from the former ScopeGlobal — old and new
// binaries interoperate mid-rollout; only the symbol name changed.
ScopeQuorum = 3;
}
// LockRequest defines the parameters for requesting a lock.
message LockRequest {
string key = 1; // The unique key representing the lock.
uint32 max_wait_period = 2; // The maximum time (in milliseconds) to wait for the lock to be granted.
uint32 max_lease_period = 3; // The maximum time (in milliseconds) the lock can be held.
uint32 priority = 4; // The priority level of the lock request.
string requester_info = 10; // Additional information about the requester.
string requester_application = 11; // The name of the application making the request.
string request_id = 12; // Idempotency key for retries of the same logical acquire request.
FenceScope fence_scope = 13; // Persistence/durability tier for fence_token. Defaults to Ephemeral.
}
// LockEvent represents an event related to the lock acquisition process.
message LockEvent {
bool success = 1; // Indicates whether the event was successful.
LockEventType event_type = 2; // The type of event that occurred.
string message = 3; // A message providing additional details about the event.
string id = 4; // The unique identifier of the lock.
string key = 5; // The key associated with the lock.
int64 lease_expires_at = 6; // The timestamp (in Unix milliseconds) when the lease expires.
int64 acquired_at = 7; // The timestamp (in Unix milliseconds) when the lock was acquired.
int64 waiting_expires_at = 8; // The timestamp (in Unix milliseconds) when the waiting period expires.
// Monotonic-per-key fence token assigned at acquire. Increments by 1 per
// successful acquisition of `key`. 0 on non-Acquired events.
//
// Held in RAM on the consistent-hash-owning node. Monotonic within that
// node's process lifetime; resets to 0 across node restart, crash, or
// hash-ring rebalance. This is intentional — see README "Known
// limitations" and "When to use waymaker" for the use cases this suits
// vs. when to reach for a different tool (etcd / ZooKeeper / Consul).
uint64 fence_token = 9;
}
// UnLockRequest defines the parameters for releasing a lock.
message UnLockRequest {
string key = 1; // The unique key representing the lock.
string id = 2; // The unique identifier of the lock to be released.
}
// UnLockResponse represents the response to an UnLock request.
message UnLockResponse {
bool success = 1; // Indicates whether the unlock operation was successful.
string result_code = 2; // A code indicating the result of the unlock operation.
string message = 3; // A message providing additional details about the unlock operation.
}
// Lease represents the details of a lock lease.
message Lease {
string id = 1; // The unique identifier of the lock.
string key = 2; // The key associated with the lock.
bool acquired = 3; // Indicates whether the lock has been acquired.
// Field 4 was historically unused; do not reuse.
reserved 4;
uint32 priority = 5; // The priority level of the lock.
int64 created_at = 6; // The timestamp (in Unix milliseconds) when the lock was created.
int64 lease_expires_at = 7; // The timestamp (in Unix milliseconds) when the lease expires.
int64 waiting_expires_at = 8;// The timestamp (in Unix milliseconds) when the waiting period expires.
// Fence token assigned at acquire. See LockEvent.fence_token caveats.
uint64 fence_token = 9;
}
// ExtendLeaseRequest defines the parameters for extending the lease of a lock.
message ExtendLeaseRequest {
string key = 1; // The unique key representing the lock.
string id = 2; // The unique identifier of the lock to extend the lease for.
uint32 lease_timeout = 3; // The additional time (in milliseconds) to extend the lease.
// The new lease expiration will be the current time + lease_timeout.
}
// ExtendLeaseResponse represents the response to an ExtendLease request.
message ExtendLeaseResponse {
bool success = 1; // Indicates whether the lease extension was successful.
string result_code = 2; // A code indicating the result of the lease extension.
string message = 3; // A message providing additional details about the lease extension.
Lease lease = 4; // The updated lease details after the extension.
}
// LeaseStatusRequest defines the parameters for retrieving the status of a lock lease.
message LeaseStatusRequest {
string key = 1; // The unique key representing the lock.
string id = 2; // The unique identifier of the lock to check the status of.
}
// LeaseStatusResponse represents the response to a LeaseStatus request.
message LeaseStatusResponse {
bool success = 1; // Indicates whether the lease status retrieval was successful.
string result_code = 2; // A code indicating the result of the lease status retrieval.
string message = 3; // A message providing additional details about the lease status retrieval.
Lease lease = 4; // The current lease details for the lock.
}
// A single (key, lock-kind) entry inside a MultiLockRequest.
message MultiLockKey {
string key = 1; // The unique key representing the lock.
bool write_lock = 2; // true = exclusive (write), false = shared (read).
}
// MultiLockRequest defines the parameters for atomically acquiring N locks.
// See MultiLock RPC docs for ordering and rollback semantics.
message MultiLockRequest {
repeated MultiLockKey keys = 1; // 1..N keys to acquire. Re-ordered server-side.
uint32 max_wait_period = 2; // Total batch deadline (ms). Per-key budget is the remainder.
uint32 max_lease_period = 3; // Per-key lease length (ms).
uint32 priority = 4; // Priority applied to every key.
string requester_info = 10;
string requester_application = 11;
string request_id = 12; // Idempotency key for the batch.
FenceScope fence_scope = 13; // Applies to every key in the batch.
}
// MultiLockResponse — unary result of a MultiLock attempt.
message MultiLockResponse {
bool success = 1; // true iff all keys were acquired.
string result_code = 2; // "ok" | "timeout" | "no_keys" | "invalid_scope" | "internal"
string message = 3; // Free-form detail; empty on success.
// Populated only on success, in lexicographic key order (the order the
// server acquired them in). On failure this is empty and any locks
// briefly held during the attempt have already been released.
repeated Lease leases = 4;
}
// ListAcquiredLocksRequest — filter for ListAcquiredLocks.
message ListAcquiredLocksRequest {
string key_prefix = 1; // Optional; empty = every held key on this node.
}
// AcquiredLock — one lock currently HELD (not waiting) on the serving node.
message AcquiredLock {
string key = 1; // The lock key.
string lock_id = 2; // The holder's unique lock id.
bool write_lock = 3; // true = exclusive (write); false = shared (read).
uint32 priority = 4; // Priority the lock was acquired at.
uint64 fence_token = 5; // Fence token assigned at acquire.
int64 lease_expires_at = 6; // Lease expiry (epoch ms).
int64 acquired_at = 7; // Acquire time (epoch ms).
string request_id = 8; // Idempotency key of the acquire.
string requester_info = 9; // Caller-supplied requester metadata (free-form string).
string requester_application = 10; // Caller-supplied application name.
}
// ListAcquiredLocksResponse — held locks on the serving node.
message ListAcquiredLocksResponse {
bool success = 1;
repeated AcquiredLock locks = 2;
}