Implement Known Endpoints persistence with EF Core insert-only tables#5625
Conversation
e45b529 to
58b2c1f
Compare
| { | ||
| var sql = """ | ||
| WITH lock_check AS ( | ||
| SELECT pg_try_advisory_xact_lock(hashtext('known_endpoints_sync')) AS acquired |
There was a problem hiding this comment.
We need locks so that if the instance is scalled out for some reason, the code still works correctly
| // Call base first to apply entity configurations | ||
| base.OnModelCreating(modelBuilder); | ||
|
|
||
| // Apply snake_case naming convention for PostgreSQL |
There was a problem hiding this comment.
Using snake_case is the official de facto standard for PostgreSQL database objects because the system automatically folds all unquoted SQL identifiers to lowercase
There was a problem hiding this comment.
Is there a particular reason for not using EFCore.NamingConventions to do this?
There was a problem hiding this comment.
Good to know, I have modified it to use it
| <ItemGroup> | ||
| <!-- Private=false & ExcludeAssets=runtime prevent repeatedly including binary dependencies of ServiceControl.Persistence and its dependencies in each persister directory --> | ||
| <ItemGroup Condition="'$(Configuration)' == 'Release'"> | ||
| <!-- Private=false & ExcludeAssets=runtime prevent repeatedly including binary dependencies of ServiceControl.Persistence |
There was a problem hiding this comment.
This is important otherwise dotnet ef migrations does not work, It can't find dependencies
| { | ||
| var sql = """ | ||
| DECLARE @lockResult INT; | ||
| EXEC @lockResult = sp_getapplock @Resource = 'known_endpoints_sync', @LockMode = 'Exclusive', @LockOwner = 'Transaction', @LockTimeout = 0; |
There was a problem hiding this comment.
Same as PostgreSQL lock but this time for SqlServer
|
|
||
| public class KnownEndpointEntity | ||
| { | ||
| public Guid Id { get; set; } |
There was a problem hiding this comment.
I think is ok to use Guid here, given that we are setting it explicitly
|
|
||
| public class KnownEndpointInsertOnlyEntity | ||
| { | ||
| public long Id { get; set; } |
There was a problem hiding this comment.
This one is the insert only table, so I want things to go fast
| using var scope = host.Services.CreateScope(); | ||
| var db = scope.ServiceProvider.GetRequiredService<PostgreSqlServiceControlDbContext>(); | ||
| await db.Database.EnsureCreatedAsync(); | ||
| await db.Database.MigrateAsync(); |
There was a problem hiding this comment.
We are using migrations now, this ensures that side is also correct.
42dc604 to
885d4d0
Compare
| // Call base first to apply entity configurations | ||
| base.OnModelCreating(modelBuilder); | ||
|
|
||
| // Apply snake_case naming convention for PostgreSQL |
There was a problem hiding this comment.
Is there a particular reason for not using EFCore.NamingConventions to do this?
| Host NVARCHAR(MAX) | ||
| ); | ||
|
|
||
| DELETE TOP (@batchSize) FROM KnownEndpointsInsertOnly |
There was a problem hiding this comment.
Can you avoid the temp table by just putting the Delete/Output into the CTE directly?
There was a problem hiding this comment.
Data-modifying CTEs are a PostgreSQL feature. SQL Server doesn't allow DELETE/INSERT/UPDATE inside a WITH clause at all.
| @@ -9,19 +8,19 @@ namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; | |||
| public class EFIngestionUnitOfWork : IIngestionUnitOfWork | |||
There was a problem hiding this comment.
Should this and the other persistence interface implementations be internal?
There was a problem hiding this comment.
we can fine tune that later, but the TL;DR; is that it doesn't really matter, this is not an API
| } | ||
| catch (DbUpdateException) | ||
| { | ||
| // A concurrent insert with the same deterministic id may have won the race |
There was a problem hiding this comment.
should this explicitly check for the key violation error type?
should it also log a debug logline?
There was a problem hiding this comment.
I have modified this to confirm that is a key violation by revalidating the data.
885d4d0 to
3362d69
Compare
Introduce `KnownEndpointEntity` and `KnownEndpointInsertOnlyEntity` tables for efficient, eventual-consistent storage of discovered endpoints. A background reconciler processes new endpoint entries from the insert-only table into the main table, handling duplicates and maintaining existing 'Monitored' state. This completes the EF Core integration for the `IMonitoringDataStore`. Updates test contexts for database teardown and draining of insert-only tables.
Refactor `InsertOnlyTableReconciler` to be non-generic, simplifying its declaration. Integrate `TimeProvider` for precise control over delays during testing. Add a `ReconcileNow` method to immediately trigger reconciliation cycles, bypassing the background service's periodic timers. This enables tests to drain insert-only tables without waiting for real time. Update persistence test contexts to leverage `FakeTimeProvider` and the new `ReconcileNow` method, reducing test setup complexity and coupling to specific reconciler implementations.
Ensures that all properties of `MessageBodyFileResult` are explicitly initialized upon object creation, preventing nullability issues and improving the integrity of message body data.
3362d69 to
510ed9c
Compare
Co-authored-by: Warwick Schroeder <warwick.schroeder@gmail.com>
Introduce
KnownEndpointEntityandKnownEndpointInsertOnlyEntitytables for efficient, eventual-consistent storage of discovered endpoints. A background reconciler processes new endpoint entries from the insert-only table into the main table, handling duplicates and maintaining existing 'Monitored' state.This completes the EF Core integration for the
IMonitoringDataStore.Updates test contexts for database teardown and draining of insert-only tables.