Skip to content

Polecat 5.36.0

Latest

Choose a tag to compare

@jeremydmiller jeremydmiller released this 05 Oct 02:19

Polecat runs under Native AOT. This morning it did not — a natively-published app threw from
DocumentStore's constructor, before writing a single document. Seven of eight shapes now pass in a
native image against SQL Server, measured by a new CI lane that publishes and runs rather than
merely building.

Thirteen issues, sixteen commits, three Weasel releases and one JasperFx release.

⚠️ Breaking changes

Six scoped behaviour changes. Minor rather than patch, with the table first — the 5.26.0 / 5.10.0
precedent.

Area Before After
Streamed Select() (ToJsonArrayAsync) — gh-722 a key absent from the stored document emitted "key": null, which threw in a consumer's deserializer for a non-nullable value type the key is omitted for a non-nullable value-type member; a genuine null on a nullable or reference member still emits null
Search over a hierarchy root — gh-729 every row materialized as the root, dropping sub-class members each row resolves to its concrete type, matching Query<Root>()
Advanced.CompletelyRemoveAllAsync() — gh-718 dropped pc_* tables only; partition schemes and functions survived — and they are database-scoped, so they outlived the schema and were shared across schemas drops the modeled partition schemes and functions too
LINQ async surface — gh-733 carried [UnconditionalSuppressMessage] asserting it was trim/AOT-safe carries propagating [RequiresUnreferencedCode] / [RequiresDynamicCode]. A breaking diagnostic change for AOT publishers only — no new warnings unless you trim or publish AOT. ⚠️ Now over-broad since LINQ works; narrowing tracked in #743
Async daemon event loading (via JasperFx 2.80.1) an event-load failure surfaced as the bare provider exception arrives wrapped in EventLoaderException. Matters if your code catches SqlException out of the daemon
Mapped concurrency version — gh-720 Metadata(m => m.Version.MapTo(...)) selected no concurrency mode, so the documented API could not be turned on the mapped member's CLR type selects the mode, as the marker interfaces do

Native AOT

See the new guide. Working natively:
store construction, schema migration, document writes, LoadAsync, LINQ reads (including enum
comparisons and child-collection filters), event append and FetchStreamAsync, and live aggregation.

Not working: a strong-typed document id (readonly record struct FooId(Guid)) — #733, measured
rather than assumed.

⚠️ The consumer contract, three items, each found by running rather than reading:

  1. root your document types with [DynamicDependency(PublicProperties, typeof(YourDoc))]. Polecat
    cannot name types it has never seen, and nothing statically reads your Id property.
  2. supply a source-generated JsonSerializerContext via ConfigureSerialization. It must list
    JasperFx.Events.Daemon.DeadLetterEvent — Polecat registers that as a document on your behalf, so
    it crosses the serializer even if you never touch dead letters.
  3. do not set InvariantGlobalization — the event paths need real globalization data.

Six walls fell to get here, each invisible until the one before it did. The recurring lesson, three
times over: a statically-referenced interface survives trimming; a reflected member does not — and
Type.GetProperty / GetMethod return null for a trimmed member rather than throwing, so the
null-forgiving operator one line later is what you actually see.

Fixes

  • gh-723 — vector, full-text and hybrid search ignored the hierarchy discriminator, so a sub-class
    search returned siblings and base rows deserialized as the sub-class asked for. Marten has the
    same bug (marten#5440); four facts added upstream in JasperFx/jasperfx#945.
  • gh-720 — a mapped Version/Revision member was ignored when seeding the expected version, so
    MapTo + optimistic concurrency failed from any session that had not loaded the document. The
    fourth independent sighting of this field after fisher#245, marten#5372 and polecat#592.
  • gh-722, gh-729, gh-718 — see the table above.
  • gh-710 / gh-721 — a large IN list travels as one JSON array. The threshold is now a floor with
    a real per-command ceiling against SqlServerMigrator.MaxParametersPerCommand, which closes the
    many small lists case that no per-fragment threshold could reach.
  • gh-733 / gh-741 — the AOT work, including a NullReferenceException that made every LINQ query
    fail natively: six handler sites reflected for HandleAsync and Task.Result, five of them latent.

Hardening and tooling

  • gh-725 — a new aot-runtime-smoke lane publishes native and runs eight shapes against SQL
    Server, printing per-shape results and stack frames. It is continue-on-error while #733 is open,
    and it found every wall in this release.
  • gh-685 — the full-text index becomes Weasel schema objects; the last document raw DDL is gone.
  • gh-724 — docs/configuration/retries.md claimed Polecat retried transient errors by default. It
    does not, and the first sample it showed was the one that loses a write under Snapshot isolation
    (marten#5528). Corrected, with PolecatRetryPredicates encoding which errors must never be replayed.
  • gh-645 — the migration guide dated vector projections to 5.29; they shipped in 5.30.0.

Dependencies

JasperFx 2.79.1 → 2.80.2 · Weasel 9.38.0 → 9.41.0

None of it inert. Weasel 9.41's non-generic IIdentification facade and strong-typed-id factory were
filed from this work (JasperFx/weasel#689, #690) and shipped the same day; weasel#694 and marten#5600
remain open from it.

Full suite: 3124 passed / 0 failed / 12 skipped.