diff --git a/CHANGELOG.md b/CHANGELOG.md
index 12c927f..3b68001 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,14 +5,25 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.0.2] - 2026-06-20
+
+### Changed
+- PostgreSQL lease service now uses configurable `Schema` and `Table` from `IRuntimeLeaseConnectionStringProvider` instead of hardcoded `public.app_runtime_leases`.
+- Added explicit missing-table error handling for PostgreSQL lease acquire/release operations.
+- Added provider-configuration validation for Redis and etcd services (required connection settings and key prefix).
+- Added DI overloads to reuse host-managed clients (`NpgsqlDataSource`, `IConnectionMultiplexer`, `EtcdClient`) for lease services.
+
## [1.0.1] - 2026-06-20
### Added
- DI registration extensions in `MaksIT.HAMode.Extensions.ServiceCollectionExtensions` for runtime instance id and backend-specific lease service wiring (`PostgreSql`, `Redis`, `Etcd`).
+- DI extension overloads that accept concrete configuration instances implementing HAMode configuration interfaces.
+- Root connector configuration interface `IRuntimeLeaseConnectionProvider`, with connector-specific interfaces inheriting it.
### Changed
- Updated package/release setup to publish `MaksIT.HAMode` as the primary distributable library for version `1.0.1`.
- Updated several dependency versions across HAMode projects.
+- Updated README backend examples to use host-defined configuration interfaces and concrete classes, instead of direct `IConfiguration["..."]` access.
## [1.0.0] - 2026-06-20
diff --git a/README.md b/README.md
index a56e6bc..1a3e19a 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ Reusable high-availability runtime coordination library for MaksIT services.
- `MaksIT.HAMode.Abstractions` namespace:
- `IRuntimeInstanceId`
- `IRuntimeLeaseService`
+ - `IRuntimeLeaseConnectionProvider` (root marker interface)
- `IRuntimeLeaseConnectionStringProvider`
- `IRuntimeLeaseRedisConnectionProvider`
- `IRuntimeLeaseEtcdConnectionProvider`
@@ -33,7 +34,8 @@ Reusable high-availability runtime coordination library for MaksIT services.
`RuntimeLeaseServiceNpgsql` expects:
-- table: `public.app_runtime_leases`
+- table: configurable via `IRuntimeLeaseConnectionStringProvider.Schema` and `IRuntimeLeaseConnectionStringProvider.Table`
+ - defaults: `public.app_runtime_leases`
- columns:
- `lease_name` (text, PK)
- `holder_id` (text)
@@ -41,13 +43,15 @@ Reusable high-availability runtime coordination library for MaksIT services.
- `acquired_at_utc` (timestamptz)
- `expires_at_utc` (timestamptz)
+If the configured table does not exist, PostgreSQL lease operations return an explicit internal error explaining which table is missing.
+
## Usage examples
### Install package
```xml
-
+
```
@@ -65,11 +69,28 @@ builder.Services.AddHAModeRuntimeInstanceId();
using MaksIT.HAMode.Abstractions;
using MaksIT.HAMode.Extensions;
-public sealed class MyPgLeaseConnectionProvider(IConfiguration cfg) : IRuntimeLeaseConnectionStringProvider {
- public string ConnectionString => cfg["Configuration:Engine:ConnectionString"]!;
+// Host project contract.
+public interface IMyPgLeaseConfiguration : IRuntimeLeaseConnectionStringProvider;
+
+// Host project concrete configuration.
+public sealed class MyPgLeaseConfiguration : IMyPgLeaseConfiguration {
+ public required string ConnectionString { get; init; }
+ public string Schema { get; init; } = "ha";
+ public string Table { get; init; } = "runtime_leases";
}
-builder.Services.AddHAModePostgreSql();
+IMyPgLeaseConfiguration pgConfiguration = new MyPgLeaseConfiguration {
+ ConnectionString = ""
+};
+
+builder.Services.AddHAModePostgreSql(pgConfiguration);
+```
+
+If you already manage a pooled PostgreSQL client in the host, pass the shared `NpgsqlDataSource`:
+
+```csharp
+var dataSource = new NpgsqlDataSourceBuilder("").Build();
+builder.Services.AddHAModePostgreSql(pgConfiguration, dataSource);
```
### Redis backend
@@ -78,30 +99,66 @@ builder.Services.AddHAModePostgreSql();
using MaksIT.HAMode.Abstractions;
using MaksIT.HAMode.Extensions;
-public sealed class MyRedisLeaseConnectionProvider(IConfiguration cfg) : IRuntimeLeaseRedisConnectionProvider {
- public string Configuration => cfg["Configuration:Redis:ConnectionString"]!;
- public string KeyPrefix => "my-app/runtime-leases:";
+// Host project contract.
+public interface IMyRedisLeaseConfiguration : IRuntimeLeaseRedisConnectionProvider;
+
+// Host project concrete configuration.
+public sealed class MyRedisLeaseConfiguration : IMyRedisLeaseConfiguration {
+ public required string Configuration { get; init; }
+ public string KeyPrefix { get; init; } = "my-app/runtime-leases:";
}
-builder.Services.AddHAModeRedis();
+IMyRedisLeaseConfiguration redisConfiguration = new MyRedisLeaseConfiguration {
+ Configuration = ""
+};
+
+builder.Services.AddHAModeRedis(redisConfiguration);
```
+If you already manage a shared Redis client in the host, pass the same `IConnectionMultiplexer`:
+
+```csharp
+var multiplexer = await ConnectionMultiplexer.ConnectAsync("");
+builder.Services.AddHAModeRedis(redisConfiguration, multiplexer);
+```
+
+Redis is schema-less, so there is no table bootstrap requirement; lease keys are isolated by `KeyPrefix`.
+
### etcd backend
```csharp
using MaksIT.HAMode.Abstractions;
using MaksIT.HAMode.Extensions;
-public sealed class MyEtcdLeaseConnectionProvider(IConfiguration cfg) : IRuntimeLeaseEtcdConnectionProvider {
- public string Endpoints => cfg["Configuration:Etcd:Endpoints"]!; // ex: http://etcd:2379
- public string? Username => cfg["Configuration:Etcd:Username"];
- public string? Password => cfg["Configuration:Etcd:Password"];
- public string KeyPrefix => "my-app/runtime-leases/";
+// Host project contract.
+public interface IMyEtcdLeaseConfiguration : IRuntimeLeaseEtcdConnectionProvider;
+
+// Host project concrete configuration.
+public sealed class MyEtcdLeaseConfiguration : IMyEtcdLeaseConfiguration {
+ public required string Endpoints { get; init; } // ex: http://etcd:2379
+ public string? Username { get; init; }
+ public string? Password { get; init; }
+ public string KeyPrefix { get; init; } = "my-app/runtime-leases/";
}
-builder.Services.AddHAModeEtcd();
+IMyEtcdLeaseConfiguration etcdConfiguration = new MyEtcdLeaseConfiguration {
+ Endpoints = "http://etcd:2379",
+ Username = null,
+ Password = null
+};
+
+builder.Services.AddHAModeEtcd(etcdConfiguration);
```
+If you already manage a shared etcd client in the host, pass the same `EtcdClient`:
+
+```csharp
+var etcdClient = new EtcdClient("http://etcd:2379");
+builder.Services.AddHAModeEtcd(etcdConfiguration, etcdClient);
+```
+
+etcd is key-space based, so there is no table bootstrap requirement; lease keys are isolated by `KeyPrefix`.
+
### Runtime acquire/release flow
```csharp
@@ -145,7 +202,7 @@ In `MaksIT.Vault.Engine.csproj`:
```xml
-
+
```
@@ -207,7 +264,7 @@ In `MaksIT.CertsUI.Engine.csproj`:
```xml
-
+
```
diff --git a/assets/badges/coverage-branches.svg b/assets/badges/coverage-branches.svg
index fa5039f..27013c9 100644
--- a/assets/badges/coverage-branches.svg
+++ b/assets/badges/coverage-branches.svg
@@ -1,5 +1,5 @@
-