PostgresLeaseStore

class PostgresLeaseStore(dataSource: DataSource, clock: Clock = Clock.systemUTC(), dispatcher: CoroutineDispatcher = VirtualThreads.dispatcher, ddl: Ddl = Ddl.AUTO, closeTimeout: Duration = CLOSE_TIMEOUT) : LeaseStore, AutoCloseable

The LeaseStore on PostgreSQL: one row per (group, partition) in wow_lease and one per (service, node), with the node's internal address and the lease groups it serves (groups), in wow_lease_member, whose key column subscription_group holds the service.

Every operation is one autocommitted statement, on a connection the store borrows once and keeps: the store's operations run one at a time on it, so a renewal never waits behind the commands for a connection of the pool they share. A connection that fails as a connection (PostgresFailures.isTransient) is given back and the failure thrown; the next operation borrows another. The pool lends the commands one connection less, and a pool that detects leaks reports this one once. An acquisition is an upsert whose update applies only when the stored lease has expired or belongs to the acquiring node; PostgreSQL re-checks that condition against the latest row after waiting for a concurrent writer, so concurrent acquisitions have exactly one winner. A release keeps the row with expiry 0, so the next acquisition continues its epoch.

Times come from clock, so they are the calling node's: keep the TTL well above the nodes' clock skew. JDBC calls run on dispatcher, virtual threads by default.

Closing it gives the connection back to the pool, once the operation that holds it, if any, is done, waiting for it at most closeTimeout; an operation after that fails. Its store closes it when it is closed (me.ahoo.wow.spi.store.Store.close).

Constructors

Link copied to clipboard
constructor(dataSource: DataSource, clock: Clock = Clock.systemUTC(), dispatcher: CoroutineDispatcher = VirtualThreads.dispatcher, ddl: Ddl = Ddl.AUTO, closeTimeout: Duration = CLOSE_TIMEOUT)

Functions

Link copied to clipboard
open suspend override fun acquire(group: String, partition: Int, owner: String, ttl: Duration): Lease?
Link copied to clipboard
open override fun close()

Gives the pinned connection back, once the operation holding it is done: an operation after that fails. It waits for that operation at most closeTimeout. An operation still running then has its connection aborted (Connection.abort) and given back, so its statement fails; one still borrowing a connection gives it back unused and fails as closed (borrow). Both steps are best effort there, logged when they fail: neither throws out of the close.

Link copied to clipboard

Claims the tables' format (PostgresFormat) and creates them when missing.

Link copied to clipboard
open suspend override fun heartbeat(service: String, node: String, address: String, ttl: Duration, groups: Set<String>): Member
Link copied to clipboard
open suspend override fun leave(service: String, node: String)
Link copied to clipboard
open suspend override fun members(service: String): List<Member>
Link copied to clipboard
open suspend override fun owners(group: String): List<Lease>
Link copied to clipboard
open suspend override fun release(group: String, partition: Int, owner: String): Boolean
Link copied to clipboard
open suspend override fun renew(group: String, partitions: Collection<Int>, owner: String, ttl: Duration): List<Lease>

One statement for every partition.