Repository navigation
Description
Goal
Let a monitoring tool query saga instances for one saga type, with filters on the saga's own properties, ordering, paging and a total count, through one abstraction across every saga storage.
The question this answers is the one an operator has with a stuck workflow: "which OrderSaga instances are in AwaitingPayment and have not changed for two days?" CritterWatch already offers exactly this for documents through IDocumentStoreDiagnostics, and wants the same for sagas.
What exists
ISagaStoreDiagnostics has GetRegisteredSagasAsync, ReadSagaAsync and ListSagaInstancesAsync(sagaTypeName, count), aggregated through IWolverineRuntime.SagaStorage.
Two limits:
ListSagaInstancesAsyncis a bounded sample, not a query. It has no filter and no count.- It has no defined order. The Marten, EF Core and RavenDB implementations are
Query<TSaga>().Take(count)with noOrderBy, so which instances come back is up to the database. Nothing can honestly call them recent. Whatever this issue decides, the ordering should be defined and documented.
Proposal
Add a query method to the saga storage abstraction, default-implemented so existing storages still load, plus a statement of what each storage can do:
Task<SagaQueryResult> QuerySagasAsync(
string sagaTypeName, DocumentQueryOptions options, CancellationToken ct);Reuse JasperFx's DocumentQueryOptions rather than inventing a second shape. DocumentQueryCriteria.ApplyCriteriaTo<T>(IQueryable<T>) already turns a Where and OrderBy string into dynamic LINQ on any IQueryable<T>, and IsTranslationFailure already recognises a provider refusing a clause. So any storage with a real IQueryable<TSaga> should get this for very little code:
| Storage | Read today | Querying |
|---|---|---|
| Marten, Polecat, Fisher | Yes | Query<TSaga>() plus ApplyCriteriaTo, or forward to the store's IDocumentStoreDiagnostics, since a saga is a document there |
| EF Core | Yes | Set<TSaga>().AsNoTracking() plus ApplyCriteriaTo |
| RavenDB | Yes | Query<TSaga>() plus ApplyCriteriaTo; needs a look at which clauses Raven's provider refuses |
Lightweight (PostgreSQL, SQL Server, MySQL, Oracle, SQLite, through DatabaseSagaStoreDiagnostics) |
Yes | No IQueryable, and the saga is a JSON body. Probably paging and ordering only, with a filter refused |
| CosmosDB | No ISagaStoreDiagnostics implementation in 6.59.1 |
Needs read support as well as querying |
The name is open: this could be a new "saga storage provider" concept that ISagaStoreDiagnostics becomes part of, or an extension of that interface.
Rules
- Refuse rather than answer wrongly. A storage that cannot honour a
Wherethrows, asIDocumentStoreDiagnostics.QueryDocumentsAsyncmust. It never returns an unfiltered page. - Say what is supported. A caller needs to know up front whether a storage can filter, so a UI offers only what works.
- Results carry a total count, and the page size is clamped.
DocumentQueryCriteria.IsAvailableis false where dynamic code is not supported, so the AOT case refuses cleanly.
Order of work
Marten, Polecat and Fisher first, since those are nearly free, then EF Core and RavenDB, then lightweight and CosmosDB. Each storage is useful alone.
Related question
A saga declares no named states today. If a saga could mark a member as its status, tools could show real states instead of inferring them from messages. That is a separate decision and is not required here.
Activity
Merge pull request #5301 from JasperFx/stoat/critterwatch-1-2/wolveri…
JasperFxGH-5296 Query saga instances by saga type: filters, ordering,…