Reference

UsePostgres

Package Trax.Effect.Data.PostgresNamespace Trax.Effect.Data.Postgres.ExtensionsSource ServiceExtensions.cs, ModelBuilderExtensions.cs

The PostgreSQL data provider. Persists train metadata, logs, manifests, the work queue, dead letters and the rest of the trax tables in a Postgres database, and migrates its schema on startup.

Signatures

public static TraxEffectBuilderWithData UsePostgres(
    this TraxEffectBuilder configurationBuilder,
    string connectionString
)
 
public static TraxEffectBuilderWithData UsePostgres(
    this TraxEffectBuilder configurationBuilder,
    string connectionString,
    Action<NpgsqlDataSourceBuilder>? configureDataSource
)

Parameters

ParameterTypeDescription
connectionStringstringThe Npgsql connection string, such as "Host=localhost;Database=trax;Username=postgres;Password=password"
configureDataSourceAction<NpgsqlDataSourceBuilder>?Called on the data source builder after Trax maps its enums and before Build(): pool size, timeouts, multiplexing. null leaves the defaults.

Returns

TraxEffectBuilderWithData, which carries the methods that need a data provider. See AddEffects.

Behavior

Unless SkipMigrations was called earlier in the chain, UsePostgres runs DatabaseMigrator.Migrate(connectionString) synchronously before it registers anything, so the database must be reachable when the host is configured. See DatabaseMigrator. It sets HasDataProvider and HasDatabaseProvider.

  • The migration's scripts wait at most five seconds for a table lock. When one gives up, the migrator runs the pending scripts again, up to ten times, then fails startup with Postgres error 55P03. See Writing Migrations.
  • Postgres refuses the NUL character in text and jsonb. Every string Trax writes through this provider has each NUL replaced with U+FFFD. A column that identifies a row (a key, a foreign key, an indexed column, a work queue entry's external id or subject key) is compared exactly, so its NUL is kept and Postgres refuses the write; WorkQueue.Create refuses a subject key holding a NUL before that. SQLite and the in-memory provider store NUL unchanged.
  • When Postgres refuses a run's row because of a value it carries (SQLSTATE 22001, 22021, 22P05 or 54000, or text the client cannot encode, such as half of a surrogate pair), the data context raises StoreRefusedContentException, and Trax writes the row again with the output replaced by {"_unrecorded": true} and the failure message and stack trace replaced by fixed text, keeping the state, end time, failure type, junction and class. An input refused on the run's first write is replaced the same way. The original error is logged, and it propagates only when the second write fails too. Lifecycle hooks still receive the real output and failure message. Any other failure propagates without a second write.

The data source

The NpgsqlDataSource is built by the container, once per service provider, with ModelBuilderExtensions.BuildDataSource(connectionString, configureDataSource), and disposed with that provider, which closes its connection pool. Two providers built from one service collection each get their own. It maps these enums to their trax schema types: TrainState, FailureClass, LogLevel, ScheduleType, DeadLetterStatus, WorkQueueStatus, MisfirePolicy, JunctionRunKind, JunctionRunState and SnapshotOwnerKind.

BuildDataSource(connectionString) and BuildDataSource(connectionString, configure) are public, for code that needs a data source mapped the same way outside the container.

Registered services

ServiceLifetimeRole
NpgsqlDataSourceSingletonThe data source above
IDbContextFactory<PostgresContext>SingletonCreates PostgresContext, Trax's EF Core context over the data source
IDataContextScopedA PostgresContext from the factory
IDataContextProviderFactorySingletonThe Postgres data context provider factory, registered as a non-toggleable effect, so it persists every run
ISqlDialectSingletonThe Postgres dialect
IQueuedWorkListenerSingletonHears trax_queued_work notifications from every host
IInvokedRunListener, ISnapshotNoticeSourceSingletonThe notifications the state machine runtime waits on
ICheckpointRowsSingleton, if none is registeredThe rows checkpoints are stored in
ITraxFeatureDbConfiguratorSingletonA DelegateFeatureDbConfigurator that points a feature's own DbContext at the same data source (UseNpgsql), so it needs no AddDbContext of the host's

Examples

services.AddTrax(trax => trax
    .AddEffects(effects => effects
        .UsePostgres("Host=localhost;Database=trax;Username=postgres;Password=password")
        .AddDataContextLogging()
    )
    .AddMediator(typeof(IShipOrderTrain).Assembly)
);

With the connection pool tuned:

services.AddTrax(trax => trax
    .AddEffects(effects => effects
        .UsePostgres(connectionString, dataSource =>
        {
            dataSource.ConnectionStringBuilder.MaxPoolSize = 50;
            dataSource.ConnectionStringBuilder.MinPoolSize = 5;
            dataSource.ConnectionStringBuilder.ConnectionIdleLifetime = 300;
        })
    )
    .AddMediator(typeof(IShipOrderTrain).Assembly)
);

See also