Table of Contents

Class PgManager

Namespace
Solitons.Data.Management.Postgres.Common
Assembly
Solitons.Core.dll

Provides an abstract base for managing Postgres databases, including operations for creating, dropping, and upgrading databases, and handling secrets and roles. This class implements the IPgManager interface.

public abstract class PgManager : IPgManager
Inheritance
PgManager
Implements
Inherited Members
Extension Methods

Remarks

Implementing classes must provide their own logic for database connection creation, extraction of connection information, construction of connection strings, execution of database upgrade scripts, managing database upgrade transactions, retrieval of database upgrade scripts, and managing secret information. It also contains robust mechanisms for handling transient database errors via retry policies and leverages the Task Parallel Library and Reactive Extensions for managing asynchronous operations and event-based programs, respectively.

Constructors

PgManager(string, PgManagerConfig, ScriptPriorityComparer)

Initializes a new instance of the PgManager class.

protected PgManager(string connectionString, PgManagerConfig config, ScriptPriorityComparer scriptPriorityComparer)

Parameters

connectionString string

The maintenance database connection string for the Postgres database.

config PgManagerConfig

The configuration for the Postgres database.

scriptPriorityComparer ScriptPriorityComparer

Determines the order in which the upgrade scripts should be executed.

Properties

ConnectionString

Gets the maintenance database connection string.

protected string ConnectionString { get; }

Property Value

string
Provides an abstract base for managing Postgres databases, including operations for creating, dropping, and upgrading databases, and handling secrets and roles. This class implements the interface.

Database

Gets the name of the target Postgres database.

public string Database { get; }

Property Value

string
Provides an abstract base for managing Postgres databases, including operations for creating, dropping, and upgrading databases, and handling secrets and roles. This class implements the interface.

Methods

BeginUpgradeTransactionAsync(CancellationToken)

Begins a new database transaction asynchronously as part of the upgrade process.

protected abstract Task<DbTransaction> BeginUpgradeTransactionAsync(CancellationToken cancellation)

Parameters

cancellation CancellationToken

A CancellationToken used to propagate notification that operations should be canceled.

Returns

Task<DbTransaction>

A Task<TResult> that represents the asynchronous operation. The task result contains a new instance of DbTransaction that is used to perform the upgrade.

Remarks

This abstract method must be implemented in a derived class to define the exact transaction behavior during the upgrade process. A transaction represents a unit of work, and in this case, is used for the database upgrade operation to ensure data consistency and recoverability.

ConnectionTestRetryPolicy(RetryPolicyArgs, CancellationToken)

Defines the retry policy for testing a database connection.

protected virtual IObservable<RetryPolicyArgs> ConnectionTestRetryPolicy(RetryPolicyArgs args, CancellationToken cancellation)

Parameters

args RetryPolicyArgs

The arguments for the retry policy, including information about the current exception and attempt number.

cancellation CancellationToken

A token that can be used to request the operation to be cancelled.

Returns

IObservable<RetryPolicyArgs>

An observable stream of RetryPolicyArgs that signals the next attempt should be made if the exception is transient and the attempt number is less than 10.

Remarks

This method will delay the next attempt by a number of milliseconds equal to the current attempt number times 100, up to a maximum of 1000 milliseconds (1 second).

ConstructConnectionString(string, DatabaseConnectionOptions)

Constructs a connection string using the specified template and options.

protected abstract string ConstructConnectionString(string template, PgManager.DatabaseConnectionOptions options)

Parameters

template string

A string representing the connection string template with placeholders for connection parameters.

options PgManager.DatabaseConnectionOptions

A PgManager.DatabaseConnectionOptions object representing the set of connection options to use when constructing the connection string.

Returns

string

A string representing the constructed connection string.

CreateDbConnection(string)

Creates a new DbConnection object with the specified connection string.

protected abstract DbConnection CreateDbConnection(string connectionString)

Parameters

connectionString string

The connection string to use for the new DbConnection object.

Returns

DbConnection

A new DbConnection object.

CreateDbIfNotExistsAsync(CancellationToken)

Asynchronously creates the database if it doesn't already exist.

protected virtual Task CreateDbIfNotExistsAsync(CancellationToken cancellation)

Parameters

cancellation CancellationToken

A cancellation token that can be used to cancel the operation.

Returns

Task

A Task representing the asynchronous operation.

Remarks

This method opens a new database connection and checks if the database, as defined by the Database property, exists. If it doesn't exist, the method executes a command to create the database.

Subclasses may override this method to provide custom database creation logic. The override should call this base implementation to ensure that the database exists before executing any additional setup logic.

This could be extended to create schemas, extensions, or to assign roles with specific privileges. Remember, any override should first call the base implementation to ensure the existence of the database.

CreateDbRetryPolicy(RetryPolicyArgs, CancellationToken)

Defines the retry policy for creating a database.

protected virtual IObservable<RetryPolicyArgs> CreateDbRetryPolicy(RetryPolicyArgs args, CancellationToken cancellation)

Parameters

args RetryPolicyArgs

The arguments for the retry policy, including information about the current exception and elapsed time since the first exception.

cancellation CancellationToken

A token that can be used to request the operation to be cancelled.

Returns

IObservable<RetryPolicyArgs>

An observable stream of RetryPolicyArgs that signals the next attempt should be made if the exception is transient and the elapsed time since the first exception is less than 5 minutes.

Remarks

This method will delay the next attempt by a number of milliseconds equal to the current attempt number times 1000, up to a maximum of 180,000 milliseconds (3 minutes).

CreateRoleRetryPolicy(RetryPolicyArgs, CancellationToken)

Defines the retry policy for creating a database role.

protected virtual IObservable<RetryPolicyArgs> CreateRoleRetryPolicy(RetryPolicyArgs args, CancellationToken cancellation)

Parameters

args RetryPolicyArgs

The arguments for the retry policy, including information about the current exception and attempt number.

cancellation CancellationToken

A token that can be used to request the operation to be cancelled.

Returns

IObservable<RetryPolicyArgs>

An observable stream of RetryPolicyArgs that signals the next attempt should be made if the exception is transient and the attempt number is less than 10.

Remarks

This method will delay the next attempt by a number of milliseconds equal to the current attempt number times 300, up to a maximum of 3000 milliseconds (3 seconds).

DropDbAsync(CancellationToken)

Drops the Postgres database with the specified name.

protected virtual Task DropDbAsync(CancellationToken cancellation)

Parameters

cancellation CancellationToken

The cancellation token to use.

Returns

Task
Provides an abstract base for managing Postgres databases, including operations for creating, dropping, and upgrading databases, and handling secrets and roles. This class implements the interface.

DropDbRetryPolicy(RetryPolicyArgs, CancellationToken)

Defines the retry policy for dropping a database.

protected virtual IObservable<RetryPolicyArgs> DropDbRetryPolicy(RetryPolicyArgs arg, CancellationToken cancellation)

Parameters

arg RetryPolicyArgs

The arguments for the retry policy, including information about the current exception and attempt number.

cancellation CancellationToken

A token that can be used to request the operation to be cancelled.

Returns

IObservable<RetryPolicyArgs>

An observable stream of RetryPolicyArgs that signals the next attempt should be made if the exception is transient and the attempt number is less than 10.

Remarks

This method will delay the next attempt by a number of seconds equal to the current attempt number, up to a maximum of 10 seconds.

ExecuteScriptIfApplicableAsync(DbConnection, Script, CancellationToken)

Executes the provided script against the given database connection if certain conditions are met.

protected abstract Task<bool> ExecuteScriptIfApplicableAsync(DbConnection connection, Script script, CancellationToken cancellation)

Parameters

connection DbConnection

The database connection on which the script will be executed.

script Script

The script to potentially be executed.

cancellation CancellationToken

A cancellation token that can be used to cancel the operation.

Returns

Task<bool>

A Task that represents the asynchronous operation. The result of the task indicates whether the script was executed.

Remarks

This method is designed to ensure the idempotency of setup scripts and one-time execution of migration scripts. Setup scripts are idempotent, meaning they can be run multiple times without causing negative effects or changing the result beyond the initial application. Migration scripts, on the other hand, are designed to be executed only once. The decision of whether or not to execute the script should be implemented in this method.

ExtractConnectionInfo(string)

Extracts the connection information from the specified connection string.

protected abstract PgConnectionInfo ExtractConnectionInfo(string connectionString)

Parameters

connectionString string

The connection string from which to extract the connection information.

Returns

PgConnectionInfo

The extracted connection information.

GenRandomPassword()

Generates a random password.

protected virtual string GenRandomPassword()

Returns

string

A random password.

GetSecret(string, CancellationToken)

Fetches the value of a secret, such as a password or a connection string, from a secure storage location.

protected IObservable<string> GetSecret(string secretKey, CancellationToken cancellation)

Parameters

secretKey string

The unique identifier of the secret to be retrieved.

cancellation CancellationToken

A CancellationToken that can be used to cancel the operation.

Returns

IObservable<string>

An observable sequence that contains the value of the secret.

Remarks

This method uses an asynchronous design pattern based on the IObservable<T> interface. It returns immediately with an IObservable<T> that can be used to track the completion of the operation and handle any exceptions that might occur. The secret storage location and the mechanism to retrieve the secret are implementation-dependent.

GetSecretIfExistsAsync(string, CancellationToken)

Gets the connection string for the specified role if it exists.

protected abstract Task<string?> GetSecretIfExistsAsync(string secretKey, CancellationToken cancellation)

Parameters

secretKey string

The secret key.

cancellation CancellationToken

The cancellation token to use.

Returns

Task<string>

A Task representing the asynchronous operation.

GetUpgradeScripts()

Gets the collection of scripts that should be executed for the database upgrade process.

protected abstract IEnumerable<Script> GetUpgradeScripts()

Returns

IEnumerable<Script>

An IEnumerable<T> of Script that represents the set of upgrade scripts.

Remarks

This abstract method should be implemented in a derived class to provide the specific upgrade scripts needed for the database. These scripts are executed during the upgrade process and could include operations such as data transformations, table alterations, or new data additions. The order of scripts in the returned sequence may influence the upgrade process, thus implementors should ensure their correct order.

IsAuthorizedAsync(DbConnection, CancellationToken)

Asynchronously checks whether the current Postgres user is authorized to create databases and roles.

public static Task<bool> IsAuthorizedAsync(DbConnection connection, CancellationToken cancellation = default)

Parameters

connection DbConnection

The active database connection to use for the check.

cancellation CancellationToken

Optional cancellation token that can be used to cancel the operation. Defaults to none.

Returns

Task<bool>

A task representing the asynchronous operation. The task result contains a boolean indicating whether the current user is authorized to create databases and roles.

Exceptions

Exception

Throws an exception if the query operation fails.

OnInvalidConnectionStringAsync(string, string, CancellationToken)

Handles the case when an invalid connection string is encountered.

protected virtual Task OnInvalidConnectionStringAsync(string error, string role, CancellationToken cancellation)

Parameters

error string

The error message.

role string

The role associated with the connection string.

cancellation CancellationToken

The cancellation token to use.

Returns

Task

A Task representing the asynchronous operation.

OnUpgradedAsync(DbTransaction, CancellationToken)

Asynchronously handles the post-upgrade actions within a transaction scope.

protected virtual Task OnUpgradedAsync(DbTransaction transaction, CancellationToken cancellation)

Parameters

transaction DbTransaction

The DbTransaction in which the upgrade has been performed.

cancellation CancellationToken

A CancellationToken used to propagate notification that operations should be canceled.

Returns

Task

A Task that represents the asynchronous operation. The task result indicates that the transaction has been committed.

Remarks

This method gets called after the database upgrade is done. By default, it commits the database transaction, finalizing the changes. Override this method to add additional steps after the upgrade process or to alter the default transaction behavior.

OnUpgradingAsync(CancellationToken)

Called during the upgrade process and can be overridden to implement custom upgrade logic.

protected virtual Task OnUpgradingAsync(CancellationToken cancellation)

Parameters

cancellation CancellationToken

A cancellation token that can be used to cancel the operation.

Returns

Task

A Task that represents the completion of this method. This base implementation immediately returns a completed task.

PerformPostUpgradeTestsAsync(CancellationToken)

Executes an optional set of tests against the database.

protected virtual Task PerformPostUpgradeTestsAsync(CancellationToken cancellation)

Parameters

cancellation CancellationToken

An instance of CancellationToken used to propagate notification that operations should be canceled.

Returns

Task

An instance of Task representing the asynchronous operation.

Remarks

This method provides a hook to perform any necessary tests against the database once the upgrade process has been completed. It is empty by default, and can be overridden in a derived class to implement specific tests. Note that this method is run asynchronously, so any implemented tests should also be asynchronous. If no tests are required, the method can be left as it is, and it will simply return a completed task when called.

RegisterLoginIfNotExistsAsync(PgConnectionInfo, string, string, CancellationToken)

Asynchronously registers a new database login if it doesn't already exist.

protected virtual Task<bool> RegisterLoginIfNotExistsAsync(PgConnectionInfo maintenanceDbConnectionParams, string username, string secretName, CancellationToken cancellation)

Parameters

maintenanceDbConnectionParams PgConnectionInfo

The connection parameters for the database maintenance operations.

username string

The username for the new login.

secretName string

The name of the secret to be stored.

cancellation CancellationToken

A cancellation token that can be used to cancel the operation.

Returns

Task<bool>

A Task that represents the asynchronous operation. The task result contains a boolean indicating whether a new secret was generated.

Remarks

This method checks the validity of the provided secret. If it is not valid or does not exist, it generates a new secret and creates a new role with the given username and new secret in the database. If the role already exists, it updates the login password of the role with the new secret.

The newly generated secret is then saved and the method returns whether a new secret was generated.

Note: Exceptions during the secret validity check are caught and logged as a warning. In such cases, the method proceeds to generate a new secret.

SaveSecretAsync(string, string, CancellationToken)

Saves the specified secret value with the specified key.

protected abstract Task SaveSecretAsync(string secretKey, string secretValue, CancellationToken cancellation)

Parameters

secretKey string

The key for the secret value.

secretValue string

The secret value to save.

cancellation CancellationToken

The cancellation token to use.

Returns

Task
Provides an abstract base for managing Postgres databases, including operations for creating, dropping, and upgrading databases, and handling secrets and roles. This class implements the interface.

TestRetryPolicy(RetryPolicyArgs, CancellationToken)

Defines the retry policy for running post-upgrade tests on the database.

protected virtual IObservable<RetryPolicyArgs> TestRetryPolicy(RetryPolicyArgs arg, CancellationToken cancellation)

Parameters

arg RetryPolicyArgs

The retry policy arguments, which include details about the current retry attempt, such as the current retry count, the exception that caused the retry, and the delay before the next retry.

cancellation CancellationToken

A CancellationToken that can be used to cancel the operation.

Returns

IObservable<RetryPolicyArgs>

An observable sequence that notifies observers about the retry policy parameters for each new retry attempt.

Remarks

This method is typically used in conjunction with the PerformPostUpgradeTestsAsync(CancellationToken) method. If PerformPostUpgradeTestsAsync(CancellationToken) encounters an exception, this method determines whether to retry the test, and if so, the delay before the next attempt. By overriding this method in a derived class, you can customize the retry behavior for post-upgrade testing.