Skip to content

Wolverine 6.59.1

Choose a tag to compare

@jeremydmiller jeremydmiller released this 30 Oct 12:56
e9110b7

This is a patch release with two fixes. Upgrading is a package bump only: there is no schema change and no API change.

SQL Server: the inbox and outbox seek on their indexes again

  • GH-5292 (#5294): Several of the SQL Server message store's per-envelope statements were scanning the whole envelope table. That cost grows with the size of the inbox and outbox, and those statements run once per message. The cause was how two kinds of value were bound as parameters.
    • Envelope ids went out as sql_variant. The statements that mark envelopes as handled, reassign them, delete them and move them to the dead letter queue all filter on the envelope id. Weasel's generic command builder has no mapping for Guid, so it bound each id as DbType.Object, which SqlClient sends as sql_variant. SQL Server then had to compare CONVERT_IMPLICIT(sql_variant, [id]) on every row, which rules out a seek on the uniqueidentifier primary key. Ids are now bound as uniqueidentifier, so each statement is a primary key seek.
    • Envelope addresses went out as nvarchar. received_at on the inbox and destination on the outbox are varchar(250) columns, but statements that filter on them bound the address as nvarchar. Under a SQL collation such as SQL_Latin1_General_CP1_CI_AS, the default on Azure SQL, that converts the column and defeats the index. The statements affected are the per-envelope updates and deletes, inbox recovery's page load, outbox loads by destination, the scheduled message updates and the duplicate check. They now bind the address as varchar.
    • DateTime parameters kept only the date. The same generic mapping typed DateTime as DbType.Date. The message store now lets SqlClient type both values itself.
    • The new test checks SQL Server's plan cache after running each of these statements. It requires that no plan contains sql_variant and that no plan converts an indexed envelope column. The full SqlServerTests suite passes (721 tests, 2 skipped).
    • PostgreSQL, MySQL, SQLite and Oracle bind exactly as they did before.
    • The underlying Weasel mappings are tracked as JasperFx/weasel#758 (Guid as sql_variant) and JasperFx/weasel#759 (DateTime as date). Wolverine works around both on its own, so this release doesn't wait for a Weasel release.

The run command no longer blocks a thread pool thread

  • JasperFx 2.96.2 (JasperFx/jasperfx#1093, fixes JasperFx/jasperfx#1092): Wolverine now depends on JasperFx 2.96.2. Before this release, the run command waited for shutdown by blocking a thread pool thread for the whole life of the application. That matters when a container's CPU limit is below one core. ProcessorCount is then 1, so the thread pool's minimum is one thread, and the run command held it. After that, every burst of work had to wait for the pool to add a thread, about 500 ms each time. The reporter saw a 200 ms Task.Delay loop wake up as much as about 950 ms late on Azure Container Apps.
    • The run command now awaits shutdown instead of blocking. It still listens for the same three signals: ApplicationStopping, Ctrl+C, and the assembly load context unloading.
    • When the host is already started (the AutoStartHost / WebApplicationFactory path), RunJasperFxCommands() used to block the calling thread. It now returns a task that completes when the host shuts down.
    • This applies to every application started with RunJasperFxCommands() or RunWolverineCommands(), whatever its persistence or transport.

Full Changelog: V6.59.0...V6.59.1