Interface ILiveRecordHandler
- Namespace
- DatabentoDotNet.Extensions.Hosting
- Assembly
- DatabentoDotNet.Extensions.Hosting.dll
Receives the records a live session decodes. Registered with AddRecordHandler<THandler>().
public interface ILiveRecordHandler
Examples
internal sealed class TradePrinter : ILiveRecordHandler
{
private readonly List<string> _batch = [];
public void OnRecord(scoped RecordRef record)
{
// Copy out what you need. The RecordRef points into the decoder's buffer and is valid
// for this call only — the next fill may shift it.
if (record.TryGet(out TradeMsg trade))
{
_batch.Add($"{DbnTime.ToInstant(trade.IndexTs)} {trade.Price} x {trade.Size}");
}
}
public async ValueTask OnFlushAsync(CancellationToken cancellationToken)
{
if (_batch.Count == 0)
{
return; // an already-completed ValueTask allocates nothing
}
await WriteAsync(_batch, cancellationToken);
_batch.Clear();
}
}
Remarks
Two methods, because there is no third option. An async method cannot take a
ref struct across an await — CS4007 — so a record can only be handed over
synchronously. OnRecord(scoped RecordRef) is that hand-over and
OnFlushAsync(CancellationToken) is where the I/O goes.
The alternative costs two allocations per record and was rejected for that.
LiveClient.RecordsAsync yields an OwnedRecord and is public; a caller who wants
it needs no help from this package. What this package promises is the guarantee
LiveAllocationTests asserts, in the one package whose reason to exist is that
guarantee.
Implementations are singletons. A DI scope per record would allocate and defeat the contract. A handler needing scoped services opens a scope inside OnFlushAsync(CancellationToken), which is where its I/O belongs anyway.
An exception from either method ends the session. Swallowing it would lose market data invisibly, which is the failure class this codebase exists to convert into loud ones. A handler that wants to carry on catches its own.
Methods
OnFlushAsync(CancellationToken)
Called once per socket fill, after every buffered record has been drained. Where I/O goes.
ValueTask OnFlushAsync(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenCancelled when the session is stopping.
Returns
Remarks
Awaiting an already-completed ValueTask allocates nothing, so a handler with nothing to flush costs nothing.
OnRecord(scoped RecordRef)
Called once per record, inside the drain. The record is valid for this call only.
void OnRecord(scoped RecordRef record)
Parameters
recordRecordRefThe record, reinterpreted in place over the decoder's buffer. Copy out what you need; do not keep the reference.