Webority.Retention
0.1.2
See the version list below for details.
dotnet add package Webority.Retention --version 0.1.2
NuGet\Install-Package Webority.Retention -Version 0.1.2
<PackageReference Include="Webority.Retention" Version="0.1.2" />
<PackageVersion Include="Webority.Retention" Version="0.1.2" />
<PackageReference Include="Webority.Retention" />
paket add Webority.Retention --version 0.1.2
#r "nuget: Webority.Retention, 0.1.2"
#:package Webority.Retention@0.1.2
#addin nuget:?package=Webority.Retention&version=0.1.2
#tool nuget:?package=Webority.Retention&version=0.1.2
Webority.Retention
Deletes finished work rows from a product's SQL tables once they pass their retention window: webhook intake, outboxes, idempotency records, side effects, job state. The tables and their rules come from the product's storage register, so the rules live in one place and a push check can verify them.
Using it in a product
1. List the work tables in the register
.webority/data-placement.json names every table with a MAX column or that grows by appending. Entries of class workflow:<days> are trimmed:
{
"schemaVersion": 1,
"tables": [
{ "table": "dbo.InboundWebhookDelivery", "class": "workflow:30", "finishedAt": "ProcessedDateTimeUtc", "status": { "column": "Status", "finished": ["Processed", "Ignored"] } },
{ "table": "dbo.NotificationOutboxMessage", "class": "workflow:30", "key": "MessageId", "finishedAt": "SentDateTimeUtc" },
{ "table": "dbo.SalesLead", "class": "record" }
]
}
| Field | Meaning |
|---|---|
table |
schema.table, or the table alone for dbo |
class |
workflow:<days> (1 to 3650) is trimmed; record, bounded-text, document-pointer, analytics and history:<years> are read and left alone |
key |
The bigint or int key column; Id when left out |
finishedAt |
The UTC time the row finished (datetime2, datetime or datetimeoffset); a row where it is null is never deleted |
status |
Optional: column and the finished values. Pending and failed statuses are left out, so those rows are never deleted |
A wrong entry, or one that names a missing table or column, fails the whole run before anything is deleted.
Give finishedAt an index (with status as its second column when the entry has one): each run reads the oldest due rows first.
2. Copy the register into the Functions output
<None Include="..\.webority\data-placement.json" Link="data-placement.json" CopyToOutputDirectory="PreserveNewest" />
3. Register and configure
services.AddWeborityRetention();
"Retention": {
"RegisterPath": "data-placement.json",
"ConnectionStringName": "WeborityDbConnection",
"Brake": { "Mode": "DryRun" }
}
| Setting | Default | Meaning |
|---|---|---|
Retention:RegisterPath |
required | The register; a relative path is read from the app's base directory |
Retention:ConnectionStringName |
required | Which ConnectionStrings:<name> to trim |
Retention:BatchSize |
2,000 | Rows per delete statement (100 to 4,000, below SQL Server's lock escalation) |
Retention:TimeBudget |
4 minutes | A run stops cleanly when it passes this; the next run carries on |
Retention:CommandTimeoutSeconds |
60 | Per statement |
Retention:Brake:Mode |
DryRun |
Live deletes; DryRun only logs what would go |
Retention:Brake:MaxPerRun |
50,000 | Most rows one run takes from one table |
Retention:Brake:MaxShareOfPopulation |
1.0 | No share limit: a quiet table can be entirely due |
Start in DryRun, read the run log, then set Retention:Brake:Mode to Live in the app settings. No deploy is needed.
4. Call it from a timer
public sealed class RetentionFunction(RetentionRunner runner)
{
[Function("Retention")]
public Task RunAsync([TimerTrigger("0 15 * * * *")] TimerInfo timer, CancellationToken cancellationToken)
{
return runner.RunAsync(cancellationToken);
}
}
RunAsync returns a RetentionRunReport with one result per table: Trimmed, NothingDue, DryRun, Refused or OutOfTime. When the brake refuses a table, the other tables are still trimmed, and the run then throws RetentionRunFailedException so it shows as failed.
How a run works
- Reads every
workflow:<days>entry and checks its table, columns and types against the live schema. - For each table, reads the row count from the partition metadata and the keys of up to
MaxPerRundue rows, oldest first. - Checks the plan with the
Webority.Jobsdestructive-run guard (jobRetention). - In
Live, deletes in batches. Each delete repeats the due check, so a row that changed after it was planned stays.
Build, test, release
dotnet restore Webority.Retention.slnx
dotnet build Webority.Retention.slnx -c Release --no-restore -v q
dotnet test --solution Webority.Retention.slnx -c Release --no-build
A merge to main publishes to nuget.org; the version of record is VersionPrefix in Directory.Build.props.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Data.SqlClient (>= 7.1.1)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.14)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.14)
- System.Security.Cryptography.Pkcs (>= 10.0.14)
- Webority.Jobs (>= 0.31.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.2.17 | 0 | 11/7/2026 |
| 0.2.16 | 164 | 11/7/2026 |
| 0.2.15 | 453 | 11/5/2026 |
| 0.2.14 | 153 | 11/5/2026 |
| 0.2.13 | 339 | 11/3/2026 |
| 0.2.12 | 163 | 11/3/2026 |
| 0.2.11 | 110 | 11/3/2026 |
| 0.2.10 | 91 | 11/2/2026 |
| 0.2.9 | 115 | 11/2/2026 |
| 0.2.8 | 200 | 11/2/2026 |
| 0.2.7 | 128 | 11/2/2026 |
| 0.2.6 | 103 | 11/1/2026 |
| 0.2.5 | 144 | 11/1/2026 |
| 0.2.4 | 95 | 11/1/2026 |
| 0.2.3 | 114 | 11/1/2026 |
| 0.2.2 | 96 | 11/1/2026 |
| 0.2.1 | 186 | 10/30/2026 |
| 0.2.0 | 99 | 10/29/2026 |
| 0.1.4 | 96 | 10/29/2026 |
| 0.1.2 | 98 | 10/28/2026 |