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 ;` 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; }