Skip to content

Commit 282726f

Browse files
committed
Fixed BackfillService
1 parent 7805a37 commit 282726f

1 file changed

Lines changed: 71 additions & 20 deletions

File tree

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using System;
2-
using System.Linq;
2+
using System.Collections.Generic;
33
using System.Threading;
44
using System.Threading.Tasks;
55
using Microsoft.EntityFrameworkCore;
@@ -24,43 +24,94 @@ public InpBsnBackfillService(IServiceProvider serviceProvider, ILogger<InpBsnBac
2424

2525
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
2626
{
27+
// Check whether the legacy inpbsn column still exists. After migration
28+
// 20260413000000_remove_inpbsn_plain_column_from_zaakrollen has run, there
29+
// is nothing left to backfill and this service exits immediately.
30+
using var scope = _serviceProvider.CreateScope();
31+
await using var db = scope.ServiceProvider.GetRequiredService<ZrcDbContext>();
32+
33+
var columnExists = await db.Database
34+
.SqlQuery<bool>($"""
35+
SELECT EXISTS (
36+
SELECT 1
37+
FROM information_schema.columns
38+
WHERE table_name = 'zaakrollen_natuurlijk_personen'
39+
AND column_name = 'inpbsn'
40+
)
41+
""")
42+
.SingleAsync(stoppingToken);
43+
44+
if (!columnExists)
45+
{
46+
_logger.LogInformation("InpBsnBackfillService: inpbsn column no longer exists, nothing to backfill.");
47+
return;
48+
}
49+
2750
const int batchSize = 5_000;
2851
int totalMigrated = 0;
2952
int batchCount;
3053

3154
do
3255
{
33-
using var scope = _serviceProvider.CreateScope();
34-
await using var db = scope.ServiceProvider.GetRequiredService<ZrcDbContext>();
56+
using var batchScope = _serviceProvider.CreateScope();
57+
await using var batchDb = batchScope.ServiceProvider.GetRequiredService<ZrcDbContext>();
3558

36-
var batch = await db.Set<NatuurlijkPersoonZaakRol>()
37-
.Where(r => r.InpBsn != null && r.InpBsnHash == null)
38-
.Take(batchSize)
59+
// Read raw inpbsn values via SQL because the entity property has been removed.
60+
var rawRecords = await batchDb.Database
61+
.SqlQuery<InpBsnRawRecord>($"""
62+
SELECT id, inpbsn AS bsn
63+
FROM zaakrollen_natuurlijk_personen
64+
WHERE inpbsn IS NOT NULL
65+
AND inpbsn_hash IS NULL
66+
LIMIT {batchSize}
67+
""")
3968
.ToListAsync(stoppingToken);
4069

41-
batchCount = batch.Count;
70+
batchCount = rawRecords.Count;
4271

43-
foreach (var record in batch)
72+
if (batchCount > 0)
4473
{
45-
record.InpBsnHash = record.InpBsn;
46-
record.InpBsnEncrypted = record.InpBsn;
47-
}
74+
var ids = new List<Guid>(batchCount);
75+
var bsnById = new Dictionary<Guid, string>(batchCount);
76+
foreach (var r in rawRecords)
77+
{
78+
ids.Add(r.Id);
79+
bsnById[r.Id] = r.Bsn;
80+
}
4881

49-
await db.SaveChangesAsync(stoppingToken);
50-
db.ChangeTracker.Clear();
82+
var entities = await batchDb.Set<NatuurlijkPersoonZaakRol>()
83+
.Where(e => ids.Contains(e.Id))
84+
.ToListAsync(stoppingToken);
5185

52-
totalMigrated += batchCount;
86+
foreach (var entity in entities)
87+
{
88+
var bsn = bsnById[entity.Id];
89+
entity.InpBsnHash = bsn;
90+
entity.InpBsnEncrypted = bsn;
91+
}
5392

54-
_logger.LogInformation("Migrated {BatchCount} InpBsn records to Hash and Encrypted (total: {TotalMigrated})", batchCount, totalMigrated);
93+
await batchDb.SaveChangesAsync(stoppingToken);
94+
batchDb.ChangeTracker.Clear();
95+
96+
totalMigrated += batchCount;
97+
98+
_logger.LogInformation(
99+
"Migrated {BatchCount} InpBsn records to Hash and Encrypted (total: {TotalMigrated})",
100+
batchCount,
101+
totalMigrated);
55102

56-
if (batchCount > 0)
57-
{
58103
await Task.Delay(50, stoppingToken);
59104
}
60-
} while (batchCount > 0);
105+
}
106+
while (batchCount > 0);
61107

62-
_logger.LogInformation("Completed InpBsn migration. Total records migrated: {TotalMigrated}", totalMigrated);
108+
_logger.LogInformation("Completed InpBsn backfill. Total records migrated: {TotalMigrated}", totalMigrated);
63109
}
64110

65-
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
111+
// Keyless projection type for EF8 SqlQuery<T>
112+
private sealed class InpBsnRawRecord
113+
{
114+
public Guid Id { get; init; }
115+
public string Bsn { get; init; }
116+
}
66117
}

0 commit comments

Comments
 (0)