AdapterPoolClient
Namespace: Meshmakers.Octo.Sdk.Common.Adapters
The pool member's half of the lease protocol (AB#4924, increment 6, concept §4).
public sealed class AdapterPoolClient : IAdapterPoolHubCallbacks, IAsyncDisposable
Inheritance Object → AdapterPoolClient
Implements IAdapterPoolHubCallbacks, IAsyncDisposable
Remarks:
Receives LeaseAsync and DrainAsync from the controller and drives one lease
at a time through a fixed sequence:
enter the lease on — from this moment the process has a tenant;
enter every IAdapterLeaseParticipant, in registration order;
run the work item;
leave every participant that was entered, in reverse order;
leave the lease scope — from this moment the process has no tenant again;
report the release to the controller.
🔴 Steps 4 and 5 run on every path, including an exception thrown out of step 2 or 3, and step 6 runs even if 4 or 5 threw. That ordering is the isolation invariant: the tenant must be gone from the process before the controller is told the member is free, because the controller's next act is to hand it another tenant. Reporting first and cleaning up afterwards would open exactly the window this design exists to close.
A second lease arriving while one is held is refused, not queued and not applied. The controller's connection registry makes that impossible by construction, so reaching it means the two views have diverged — and the safe answer to "I may already be serving somebody else" is never "serve them both".
Properties
IsDraining
Whether this member was asked to drain and takes no further lease.
public bool IsDraining { get; }
Property Value
DrainReason
Why this member drains — the controller's drain request or a lease it could not leave
cleanly — or null while it is not draining (AB#5864).
public string DrainReason { get; }
Property Value
IsDrainedAndIdle
Whether this member is draining and has nothing left to finish: no lease is running and no release is still being reported (AB#5864). From this moment the process serves no purpose — it takes no lease and holds no tenant — so AdapterPoolMemberDrainExitService stops it and the pool workload restarts it as a fresh, clean process.
public bool IsDrainedAndIdle { get; }
Property Value
Remarks:
🔴 The lease gate, not , is the "nothing left" test. is cleared before the release is reported (the tenant has to be gone first), so a process stopped on it could die between the two — and the controller would then see a disconnect while it still holds the lease, interrupt the execution and run work again that had already completed. The gate is held until the report has been sent.
RegistrationState
Whether the controller currently holds a registration for this member: written on every registration attempt and on every heartbeat the controller rejects (AB#4924 AP-I5).
public IAdapterHubRegistrationState RegistrationState { get; }
Property Value
RecentlyLeasedTenantIds
The tenants whose in-process state may be stale after the management connection was lost: the tenant of the lease running right now and the tenant of the last lease entered, without duplicates. Empty for a member that never held a lease.
public IReadOnlyList<string> RecentlyLeasedTenantIds { get; }
Property Value
IsResumptionUnsupported
Whether the controller is known not to support ResumePoolMemberAsync on the current
connection (AB#5826). The member then defers its registration while a lease runs.
public bool IsResumptionUnsupported { get; }
Property Value
PendingReleaseCount
How many releases are waiting to be re-sent to the controller (AB#5826).
public int PendingReleaseCount { get; }
Property Value
Constructors
AdapterPoolClient(IAdapterLeaseScope, IAdapterPoolHubClient, IEnumerable<IAdapterLeaseParticipant>, IAdapterLeaseWorkItem, IOptions<AdapterPoolMemberOptions>, ILogger<AdapterPoolClient>, INodeSchemaRegistry, IPipelineSchemaGenerator, IAdapterHubRegistrationState)
Constructor.
public AdapterPoolClient(IAdapterLeaseScope leaseScope, IAdapterPoolHubClient poolHubClient, IEnumerable<IAdapterLeaseParticipant> participants, IAdapterLeaseWorkItem workItem, IOptions<AdapterPoolMemberOptions> options, ILogger<AdapterPoolClient> logger, INodeSchemaRegistry nodeSchemaRegistry, IPipelineSchemaGenerator pipelineSchemaGenerator, IAdapterHubRegistrationState registrationState)
Parameters
leaseScope IAdapterLeaseScope
The process's lease-aware tenant scope.
poolHubClient IAdapterPoolHubClient
The management connection to the controller.
participants IEnumerable<IAdapterLeaseParticipant>
Everything that has to be set up and torn down per lease.
workItem IAdapterLeaseWorkItem
What to run while the lease is held.
options IOptions<AdapterPoolMemberOptions>
The pool this member belongs to.
logger ILogger<AdapterPoolClient>
Logger.
nodeSchemaRegistry INodeSchemaRegistry
The nodes this member can execute; null when the host composed no data pipeline.
pipelineSchemaGenerator IPipelineSchemaGenerator
The composite pipeline schema generator; null when the host composed no data pipeline.
registrationState IAdapterHubRegistrationState
Where this member records whether the controller holds a registration for it (AB#4924
AP-I5). Read by the readiness check and the recovery watchdog; a private instance when the
host registered none.
Methods
OnNewConnection()
Forgets what the previous connection taught about the controller. Called on every new connection: the controller pod behind it may be a different build.
public void OnNewConnection()
ResumeAsync()
Registers this process while it is still running a lease, so the controller takes the lease over instead of losing it (AB#5826) — after a controller restart or a dropped connection.
public Task<PoolMemberRegistrationResultDto> ResumeAsync()
Returns
Task<PoolMemberRegistrationResultDto>
The controller's answer; the plain registration's answer when the lease ended in the
meantime; or null when the controller does not support resumption (or refused it) —
the caller then defers the registration until the lease is released, as before AB#5826.
Remarks:
🔴 Held under the same gate as the release report, and the running lease is read inside it: either the resumption reaches the controller before the release of that lease, or the lease is already over and the member registers idle. A release overtaking the resumption would let the controller complete the execution and then record the member busy with a lease that ended — a member that takes no work until the TTL reaper drains it.
FlushPendingReleasesAsync()
Re-sends releases that could not be reported while the connection was down (AB#5826).
public Task<int> FlushPendingReleasesAsync()
Returns
Task<Int32>
How many were delivered.
Remarks:
Called on every (re)connect before the member registers, and on every heartbeat tick while the connection is up. A transport failure keeps the rest for the next attempt; a controller that refuses the method drops them (it cannot apply them anyway).
RegisterAsync()
Registers this process as a member of its configured pool.
public Task<PoolMemberRegistrationResultDto> RegisterAsync()
Returns
Task<PoolMemberRegistrationResultDto>
The controller's answer, or null when the controller does not support the hub.
LeaseAsync(LeaseDto)
public Task LeaseAsync(LeaseDto lease)
Parameters
lease LeaseDto
Returns
DrainAsync(String)
public Task DrainAsync(string reason)
Parameters
reason String
Returns
HeartbeatAsync()
Sends one heartbeat, naming the lease this member believes it holds.
public Task HeartbeatAsync()
Returns
Remarks:
🔴 AB#4924 AP-I5 — a heartbeat the controller rejects means the controller holds no registration for this connection (a newer controller says so explicitly, an older one does not know the method). Either way the member is not leasable, so the registration state is cleared and AdapterPoolMemberService registers again on its next tick instead of heartbeating into a registration that does not exist.
DisposeAsync()
public ValueTask DisposeAsync()