Repository navigation
Three unrelated things: a SQL Server advisory lock that stopped reporting locks it had lost, the other half of 9.41's Native AOT work, and table storage parameters on PostgreSQL.
Full upgrade guide: https://weasel.jasperfx.net/release-9-42
Nothing in this release is a breaking change. Nothing public was removed and no signature changed. One behaviour does change, deliberately — and it is the fix rather than a side effect.
⚠️ SQL Server advisory locks end with their session — #695
Thanks to @NielsAudoor. If you run Polecat HotCold, or anything else built on Weasel.SqlServer.AdvisoryLock, take this release.
AdvisoryLock holds every lock as a session-scoped sp_getapplock on one connection. When SQL Server ends that session — failover, gateway reconfiguration, a killed session — it releases all of them, and HasLock went on returning true anyway. Another node then took the lock and both ran the shard. In a three-replica load test, 22,000 outbox events were processed more than once.
The root cause is one character of pattern matching, which reads as correct: _conn is not { State: ConnectionState.Closed } is true when _conn is null, because a null does not match the pattern.
What changes for callers:
HasLocknow needs a live connection — a node that lost its session reportsfalse, so the coordinator re-attains, fails, and stops the agents it no longer owns.- An
APPLOCK_MODEmonitor runs every 5 seconds while locks are held, always on and not yet configurable. The probe short-circuits when no locks are held. TryAttainLockAsyncpasseslockTimeoutMs: 0, so it no longer waits for a lock another node holds. This affects every caller, not only Polecat's coordinator.- A lock you already hold is no longer taken twice.
sp_getapplockis reentrant, and the oldList<int>kept a duplicate — so a release freed one hold and left the lock held on the server while Weasel believed it was free.
PostgreSQL table storage parameters — #696, #698
Thanks to @lahma for #696. A table can now declare fillfactor, the autovacuum_* family and the rest of the reloptions, and Weasel keeps them in step like any other part of the table — written on create, read back from pg_class.reloptions, and compared by the delta.
table.WithFillFactor(70)
.WithAutovacuum(vacuumScaleFactor: 0.01, insertScaleFactor: 0.02)
.WithParallelWorkers(4)
.WithStorageParameter(StorageParameterNames.VacuumTruncate, false);Only the parameters the table declares are compared. One that exists in the database but is not declared is never reset, because a DBA may have set it deliberately. Values compare numerically when both sides are numbers, so 0.05 and 0.050 do not produce a permanent false diff. For a partitioned table PostgreSQL rejects parameters on the parent, so they are written on every partition instead.
StorageParameterNames is not only convenience: StorageParameters keys are case sensitive while the DDL writer normalizes to lower case, so two spellings would render as one duplicated setting that PostgreSQL rejects with 22023. Weasel now refuses that itself, and the constants make it unreachable.
The rest of Native AOT identity — #697
From #694. 9.41 added the Identifications factory and gave ForValueType a reflected fallback; five of the other six factories still threw under Native AOT:
System.NotSupportedException: 'Weasel.Core.Identity.SequentialGuidIdentification`1[Doc]'
is missing native code or metadata.
9.41's reasoning was that these close a generic over the document type alone, a document type is a class, and reference-type instantiations share a canonical body. That is half the rule — they share one only if ILC generated it, and nothing statically referenced those strategies. [DynamicDependency] on each factory roots the canonical body and the constructor metadata.
No API changed. If you are not publishing natively, nothing here affects you.