277 lines
15 KiB
Protocol Buffer
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;
|
|
}
|