Add safe audit-event partition maintenance - #90
Merged
Conversation
The audit table grows without bound; the append-only guarantee keeps its rows honest but does nothing about their size. A retention sweep on a single non-partitioned table becomes a DELETE the size of the table, and the auditor is the person who notices. Postgres native range partitioning by OccurredAt keeps each month's rows in a child table; a retention sweep is now a metadata operation. What changed: - DatabasePartitioning.EnsurePartitionedAuditAsync drops the EF-created audit_events table and recreates it as a partitioned parent. The DDL is the only place that knows the shape. - DatabasePartitioning.EnsureCurrentAndNextMonthAsync pre-creates the current and next month, so a row whose OccurredAt lands just after a boundary has a partition to land in. - LakeWrightDbContext's primary key on AuditEvent becomes (Id, OccurredAt). Postgres requires every unique constraint on a partitioned table to include the partition key, and the Id + month combination is the only identity that survives across months. - The (OrganizationId, OccurredAt) index moves from the parent to each child partition. Reads still go through 'SELECT FROM audit_events' — Postgres native partitioning makes parent and children look like one table to SQL clients. - PostgresFixture calls EnsurePartitionedAuditAsync after EnsureCreatedAsync so every test database is partitioned; the harness smoke and the test suite both exercise the path. - AuditPartitionTests assert the partitioned-table property, the current/next-month partitions exist, and a round-trip insert lands in the right place. - ADR 0016 records the decision and the breaking change to the primary key. The migration is a one-time cost: a production deploy that runs against a database with data has to copy rows into the partitioned parent before the swap. The shipped manager does not do this copy; tests run against fresh databases, and the only place an existing audit table exists today is the harness's ephemeral container. A production migration is a separate change.
The recent work (cost proxy, load harness, Postgres sizing, harness auth) was reviewed with the post-ship-review lenses: correctness, security/ tenancy, performance, requirements completeness. The review found one bug already fixed (the harness's Member role) and lists the gaps that remain: production migration of existing audit rows, tightening the harness's SLO gates to production-anchored values, Key Vault for the Bicep's admin password, and a coverage re-measurement that the milestone's new tests are excluded from. The review is the maintainer's self-review, which the ROADMAP records as the project's standing weakness. A second pair of eyes is the next maintenance task.
The gap analysis (2.2) identified that LakeWright's hardcoded TenantId.ToString() for external_value produces a token that always has the same scope for a given tenant. A tenant whose data scope narrows keeps seeing the old rows for the full 24-hour result-cache lifetime. This commit adds the feature the gap calls out, the same way the gap recommends it: - TenantContext gains an optional . The Create method accepts the value and validates it: no , , or (reserved in the claim format; the delimiter is the only delimiter that survives both the GUID body and the version value, verified live this session). - The broker (DashboardTokenBroker.IssueAsync) composes when is non-empty; otherwise the bare id. The isolation property the existing code defends — a caller can never choose the external_value freely — remains intact: the value comes from the tenant, which comes from the resolver, not from the request. - The change is backward-compatible: when is null, produces exactly the previous value. This closes the highest-value design gap from the gap analysis, without forcing VRM-specific policy into the library. It does not close 3.2, 3.4, or the .NET 10 / preview-release blockers, which remain open as separate work.
Signed-off-by: Ivan Vydrin <ivan.vydrin.99@gmail.com> # Conflicts: # src/LakeWright.Core/Tenancy/TenantContext.cs # src/LakeWright.Embedding/DashboardTokenBroker.cs
Signed-off-by: Ivan Vydrin <ivan.vydrin.99@gmail.com>
Signed-off-by: Ivan Vydrin <ivan.vydrin.99@gmail.com> # Conflicts: # tests/LakeWright.TenantIsolation.Tests/PostgresFixture.cs
Signed-off-by: Ivan Vydrin <ivan.vydrin.99@gmail.com>
Signed-off-by: Ivan Vydrin <ivan.vydrin.99@gmail.com>
Signed-off-by: Ivan Vydrin <ivan.vydrin.99@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a production-oriented, migration-role-only PostgreSQL maintenance tool that safely converts
audit_eventsto monthly range partitions, validates the migrated copy, supports explicit finalize/rollback lifecycles, and applies seven-year retention without exposing privileged DDL through the shipped LakeWright packages.What changed
LakeWright.DatabaseMaintenancetool and internal maintenance core.audit_events_unpartitioned_backupuntil validation/finalization.Iduniqueness through a protected registry andSECURITY DEFINERinsert trigger.Safety properties verified
Current verification
dotnet restore LakeWright.slnx --locked-modedotnet restore scripts/load/Lakewright.LoadHarness/Lakewright.LoadHarness.csproj --locked-modedotnet build LakeWright.slnx -c Release --no-restore— 0 warnings, 0 errorsdotnet build scripts/load/Lakewright.LoadHarness/Lakewright.LoadHarness.csproj -c Release --no-restore— 0 warnings, 0 errorsdotnet format LakeWright.slnx --no-restore --verify-no-changesThe complete whole-application regression, Docker/sample smoke, live non-mutating Databricks bundle checks, package consumer smoke, and release evidence will run on merged
mainbefore publishing.