Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,26 @@ jobs:
tests/CSharpDB.Admin.Reports.Tests/CSharpDB.Admin.Reports.Tests.csproj
tests/CSharpDB.Admin.Forms.Tests/CSharpDB.Admin.Forms.Tests.csproj
tests/CSharpDB.Api.Tests/CSharpDB.Api.Tests.csproj
tests/CSharpDB.Benchmarks.Tests/CSharpDB.Benchmarks.Tests.csproj
tests/CSharpDB.Cli.Tests/CSharpDB.Cli.Tests.csproj
tests/CSharpDB.CodeModules.Tests/CSharpDB.CodeModules.Tests.csproj
tests/CSharpDB.Daemon.Tests/CSharpDB.Daemon.Tests.csproj
tests/CSharpDB.Data.Tests/CSharpDB.Data.Tests.csproj
tests/CSharpDB.DataGeneration.Tests/CSharpDB.DataGeneration.Tests.csproj
tests/CSharpDB.DataPrivacy.Tests/CSharpDB.DataPrivacy.Tests.csproj
tests/CSharpDB.DevOps.Tests/CSharpDB.DevOps.Tests.csproj
tests/CSharpDB.EntityFrameworkCore.Tests/CSharpDB.EntityFrameworkCore.Tests.csproj
tests/CSharpDB.EntityFrameworkCore.Tools.Tests/CSharpDB.EntityFrameworkCore.Tools.Tests.csproj
tests/CSharpDB.Mcp.Tests/CSharpDB.Mcp.Tests.csproj
tests/CSharpDB.Migration.Access.Tests/CSharpDB.Migration.Access.Tests.csproj
tests/CSharpDB.Migration.Compatibility.Tests/CSharpDB.Migration.Compatibility.Tests.csproj
tests/CSharpDB.Migration.DualRun.Tests/CSharpDB.Migration.DualRun.Tests.csproj
tests/CSharpDB.Migration.Files.Tests/CSharpDB.Migration.Files.Tests.csproj
tests/CSharpDB.Migration.LiteDb.Tests/CSharpDB.Migration.LiteDb.Tests.csproj
tests/CSharpDB.Migration.MySql.Tests/CSharpDB.Migration.MySql.Tests.csproj
tests/CSharpDB.Migration.Retained.Tests/CSharpDB.Migration.Retained.Tests.csproj
tests/CSharpDB.Migration.SqlServer.Tests/CSharpDB.Migration.SqlServer.Tests.csproj
tests/CSharpDB.Migration.Sqlite.Tests/CSharpDB.Migration.Sqlite.Tests.csproj
tests/CSharpDB.Migration.Tests/CSharpDB.Migration.Tests.csproj
tests/CSharpDB.Observability.Tests/CSharpDB.Observability.Tests.csproj
tests/CSharpDB.Pipelines.Tests/CSharpDB.Pipelines.Tests.csproj
Expand Down Expand Up @@ -251,7 +257,29 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y clang zlib1g-dev

- name: Publish NativeAOT library
run: dotnet publish src/CSharpDB.Native/CSharpDB.Native.csproj -c Release -r ${{ matrix.rid }}
run: dotnet publish src/CSharpDB.Native/CSharpDB.Native.csproj -c Release -r ${{ matrix.rid }} -p:ILLinkTreatWarningsAsErrors=true -p:IlcTreatWarningsAsErrors=true

- name: Use private link-free temporary directory for native archive smoke (macOS)
if: runner.os == 'macOS'
shell: bash
run: |
temp_directory="$(mktemp -d /private/tmp/csharpdb-native-archive.XXXXXX)"
chmod 700 "${temp_directory}"
echo "TMPDIR=${temp_directory}" >> "${GITHUB_ENV}"

- name: Publish native archive SQL smoke
run: dotnet publish tests/CSharpDB.Native.ArchiveSmoke/CSharpDB.Native.ArchiveSmoke.csproj -c Release -r ${{ matrix.rid }} --self-contained true -p:PublishAot=true -o artifacts/native-archive-smoke/${{ matrix.rid }}

- name: Run published native archive SQL smoke
shell: pwsh
run: |
$executableName = if ($IsWindows) { 'CSharpDB.Native.ArchiveSmoke.exe' } else { 'CSharpDB.Native.ArchiveSmoke' }
$executable = Join-Path 'artifacts/native-archive-smoke/${{ matrix.rid }}' $executableName
$library = [IO.Path]::GetFullPath('src/CSharpDB.Native/bin/Release/net10.0/${{ matrix.rid }}/publish/${{ matrix.lib }}')
if (-not (Test-Path -LiteralPath $executable -PathType Leaf)) { throw "Archive smoke executable missing: $executable" }
if (-not (Test-Path -LiteralPath $library -PathType Leaf)) { throw "Native library missing: $library" }
& $executable $library
if ($LASTEXITCODE -ne 0) { throw "Native archive SQL smoke failed with exit code $LASTEXITCODE." }

- name: Publish observability trim and NativeAOT smoke
run: dotnet publish tests/CSharpDB.Observability.TrimSmoke/CSharpDB.Observability.TrimSmoke.csproj -c Release -r ${{ matrix.rid }} --self-contained true -p:PublishAot=true -o artifacts/observability-aot/${{ matrix.rid }}
Expand Down
2 changes: 2 additions & 0 deletions CSharpDB.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
<Project Path="tests/CSharpDB.Tests/CSharpDB.Tests.csproj" />
<Project Path="tests/CSharpDB.Observability.Tests/CSharpDB.Observability.Tests.csproj" />
<Project Path="tests/CSharpDB.Observability.TrimSmoke/CSharpDB.Observability.TrimSmoke.csproj" />
<Project Path="tests/CSharpDB.Native.ArchiveSmoke/CSharpDB.Native.ArchiveSmoke.csproj" />
<Project Path="tests/CSharpDB.Pipelines.Tests/CSharpDB.Pipelines.Tests.csproj" />
<Project Path="tests/CSharpDB.Data.Tests/CSharpDB.Data.Tests.csproj" />
<Project Path="tests/CSharpDB.EntityFrameworkCore.Tests/CSharpDB.EntityFrameworkCore.Tests.csproj" />
Expand Down Expand Up @@ -115,6 +116,7 @@
<Project Path="tests/CSharpDB.CodeModules.Tests/CSharpDB.CodeModules.Tests.csproj" />
<Project Path="tests/CSharpDB.ExtensionSandbox.Worker/CSharpDB.ExtensionSandbox.Worker.csproj" />
<Project Path="tests/CSharpDB.Benchmarks/CSharpDB.Benchmarks.csproj" />
<Project Path="tests/CSharpDB.NetworkBenchmarks/CSharpDB.NetworkBenchmarks.csproj" />
<Project Path="tests/CSharpDB.Benchmarks.Tests/CSharpDB.Benchmarks.Tests.csproj" />
<Project Path="tests/CSharpDB.DataGen/CSharpDB.DataGen.csproj" />
</Folder>
Expand Down
88 changes: 43 additions & 45 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,45 @@
# What's New

## CSharpDB 4.6.6

CSharpDB 4.6.6 improves durable write throughput, prepared queries, and bounded
primary-key reads while strengthening reader snapshot, buffered-row, and concurrent
WAL-read correctness.
These notes cover changes since v4.6.5.

### Query and write improvements

- Reuse prepared SELECT plans across parameter values and support bounded integer
primary-key ranges through pooled readers and ordinary queries.
- Preserve reader catalog snapshots across commits and checkpoints, including
schema metadata used by existing readers.
- Preserve committed row-count metadata during writes and restore it after
publication, avoiding unnecessary full-table scans for reader `COUNT(*)` queries.
- Reduce table-scan, LIMIT, snapshot-pager, and leaf-prefetch allocations. Forward
intact row batches while preserving ownership and mixed row/batch traversal.
- Streamline primary-key mutations and INSERT execution by reusing column ordinals,
avoiding per-row observer allocations, and skipping redundant canonical value
conversions.
- Reduce durable-write checkpoint stalls and improve WAL/checkpoint lifecycle
handling and storage-page ownership.

### Storage correctness

- Prevent checkpoint reset or retained-tail compaction from invalidating WAL
offsets used during write-conflict resolution. This fixes a concurrency race
that could cause short WAL reads or return incorrect page contents.
- Defer checkpoint finalization while durable commits still have unpublished WAL
offsets, preserving correct reads after those commits complete.
- Add deterministic regression coverage for reset and compaction, including stale
offsets that remain within file bounds, cancellation, lock release, and forward
progress. The database and WAL formats are unchanged.

### Dependencies and maintenance

- Update gRPC/Protobuf, SQL Server migration dependencies, WebView2, OpenTelemetry,
Scalar, and test tooling.
- Retain Microsoft.OpenApi 2.12.0 for compatibility with
Microsoft.AspNetCore.OpenApi 10.0.12 on .NET 10.

### Performance guardrails

- Validate performance evidence integrity and reject incomplete or inconsistent
comparison evidence.
## CSharpDB 4.7.0

CSharpDB 4.7.0 establishes shared SDK and ADO.NET gRPC connection/session handling
and fixes archive-backed SQL in the compiled native library.
These notes cover changes since v4.6.6.

### Remote client foundation

- Share physical gRPC clients between SDK rentals and pooled ADO.NET connections
with matching options. Bound pool capacity, honor cancellation while waiting,
and isolate endpoints, credentials, routes, and caller-owned HTTP clients.
- Add explicit native HTTP/2 gRPC selection and experimental persistent command
sessions for SQL and transactions. Existing Auto/unary defaults remain in
place; streaming requires an explicit opt-in.
- Preserve transaction ownership, private temporary-table state, result schemas,
and logical column types across rentals and sessions. Drain active operations
before returning a client, reject stale rentals, and clean up unfinished
transactions without automatically replaying commands after disconnect.
- Prevent a queued transaction begin from blocking a schema owner's commit or
rollback. Cancel and drain pending begins during client disposal, and clean up
transactions that cannot be registered after cancellation or shutdown.
- Preserve the selected gRPC call mode when Admin clones routed client options.

### Archive and dependency fixes

- Fix external archive table registration in the NativeAOT library by generating
schema and manifest JSON metadata. Keep existing archive JSON bytes, enum
validation, legacy omitted-field defaults, and explicit-null rejection.
- Add a published NativeAOT archive SQL smoke covering scans, primary-key lookup,
declared types, and close/reopen persistence through the C API. Fail native
publication on trimming or AOT warnings.
- Support private migration reject artifacts on Linux tmpfs, including RAM-backed
temporary directories, while retaining owner, permission, link and ACL checks.
- Update the optional SQL Server migration adapter's PKCS dependency to 9.0.22
and keep its exact bundle inventories and third-party notice aligned.
- Include all 27 solution test modules in the ordinary pull-request CI matrix.

### Scope and support

Persistent streaming remains experimental and opt-in. This release does not
include the custom TCP prototype or remote prepared-statement handles, and no
verified network performance improvement is claimed. The native archive smoke
checks the C API; it does not establish remote gRPC support under NativeAOT.
4 changes: 2 additions & 2 deletions docs/releases/changelog-publication.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"verifiedOn": "2026-10-12",
"source": "https://api.github.com/repos/MaxAkbar/CSharpDB/releases",
"notesReview": {
"version": "4.6.6",
"sha256": "0c44953b85d05e17495662f40795fd2704124b9c5fbeba0d0e9fb960218c3628"
"version": "4.7.0",
"sha256": "d7e25f8ffa9e33627e37c50bcd3b4df48ff040ed41b68519b8d57343da196ea1"
},
"releases": [
{
Expand Down
2 changes: 1 addition & 1 deletion scripts/Publish-CSharpDbSqlServerMigrationBundle.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function Assert-ReviewedWorkerPackageClosure {
'System.Configuration.ConfigurationManager/9.0.18'
'System.Diagnostics.EventLog/9.0.18'
'System.IdentityModel.Tokens.Jwt/8.16.0'
'System.Security.Cryptography.Pkcs/9.0.18'
'System.Security.Cryptography.Pkcs/9.0.22'
'System.Security.Cryptography.ProtectedData/9.0.18'
'System.Threading.RateLimiting/9.0.18'
) | Sort-Object
Expand Down
2 changes: 1 addition & 1 deletion scripts/Test-SqlServerMigrationIsolation.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ function Assert-ReviewedWorkerPackageClosure {
'System.Configuration.ConfigurationManager/9.0.18'
'System.Diagnostics.EventLog/9.0.18'
'System.IdentityModel.Tokens.Jwt/8.16.0'
'System.Security.Cryptography.Pkcs/9.0.18'
'System.Security.Cryptography.Pkcs/9.0.22'
'System.Security.Cryptography.ProtectedData/9.0.18'
'System.Threading.RateLimiting/9.0.18'
) | Sort-Object
Expand Down
2 changes: 2 additions & 0 deletions src/CSharpDB.Admin/Services/DatabaseClientHolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,8 @@ private static CSharpDbClientOptions CloneOptions(
ConnectionString = options.ConnectionString,
DataSource = options.DataSource,
HttpClient = options.HttpClient,
GrpcMode = options.GrpcMode,
GrpcCallMode = options.GrpcCallMode,
ApiKey = options.ApiKey,
ApiKeyHeaderName = options.ApiKeyHeaderName,
RouteContext = routeContext,
Expand Down
5 changes: 4 additions & 1 deletion src/CSharpDB.Client/CSharpDB.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>CSharpDB.Client</RootNamespace>
<Description>Unified CSharpDB client SDK with pluggable transports (Direct, HTTP, gRPC, TCP, Named Pipes).</Description>
<Description>Unified CSharpDB client SDK with Direct, HTTP and gRPC transports.</Description>
</PropertyGroup>

<ItemGroup>
Expand All @@ -20,6 +20,9 @@
<ItemGroup>
<InternalsVisibleTo Include="CSharpDB.DevOps" />
<InternalsVisibleTo Include="CSharpDB.Tests" />
<InternalsVisibleTo Include="CSharpDB.Daemon.Tests" />
<InternalsVisibleTo Include="CSharpDB.Data" />
<InternalsVisibleTo Include="CSharpDB.Data.Tests" />
</ItemGroup>

<ItemGroup>
Expand Down
28 changes: 27 additions & 1 deletion src/CSharpDB.Client/CSharpDbClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public sealed class CSharpDbClient :
ICSharpDbTableArchiveProgressExporter,
ICSharpDbTransactionalSnapshotReader,
ICSharpDbExclusiveSessionProvider,
ICSharpDbTransactionalSchemaIdentityWriter
ICSharpDbTransactionalSchemaIdentityWriter,
IScalarProjectionClient
{
private readonly ICSharpDbClient _inner;

Expand All @@ -31,6 +32,24 @@ public static ICSharpDbClient Create(CSharpDbClientOptions options)
return new CSharpDbClient(ClientTransportResolver.Create(options));
}

/// <summary>Rents a validated gRPC connection. Dispose the client to return it to the shared pool.</summary>
public static async ValueTask<ICSharpDbClient> RentAsync(
CSharpDbClientOptions options, int maxPoolSize = 32, CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(options);
return new CSharpDbClient(await GrpcClientPoolRegistry.RentAsync(options, maxPoolSize, ct).ConfigureAwait(false));
}

/// <summary>Retires all gRPC pools for an endpoint. Active rentals are disposed on return.</summary>
public static ValueTask ClearPoolAsync(string endpoint)
{
ArgumentException.ThrowIfNullOrWhiteSpace(endpoint);
return GrpcClientPoolRegistry.ClearAsync(endpoint.Trim());
}

/// <summary>Retires all shared gRPC pools.</summary>
public static ValueTask ClearAllPoolsAsync() => GrpcClientPoolRegistry.ClearAsync();

public static ICSharpDbShardAdminClient CreateShardAdmin(CSharpDbClientOptions options)
{
ArgumentNullException.ThrowIfNull(options);
Expand Down Expand Up @@ -58,6 +77,13 @@ public static ICSharpDbShardDirectoryClient CreateShardDirectoryClient(CSharpDbC
}

public string DataSource => _inner.DataSource;
bool IScalarProjectionClient.SupportsScalarProjection
=> _inner is IScalarProjectionClient { SupportsScalarProjection: true };
ValueTask<ScalarProjectionResult> IScalarProjectionClient.ExecuteScalarProjectionAsync(
string sql, string? transactionId, CancellationToken cancellationToken)
=> _inner is IScalarProjectionClient { SupportsScalarProjection: true } scalar
? scalar.ExecuteScalarProjectionAsync(sql, transactionId, cancellationToken)
: throw new NotSupportedException("This connection does not support scalar projection.");
public Task<DefinitionCatalogPage> ReadDefinitionCatalogAsync(string? continuationToken = null, int pageSize = 64, CancellationToken ct = default)
=> _inner is ICSharpDbDefinitionCatalogReader reader
? reader.ReadDefinitionCatalogAsync(continuationToken, pageSize, ct)
Expand Down
2 changes: 2 additions & 0 deletions src/CSharpDB.Client/CSharpDbClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public sealed class CSharpDbClientOptions
public string? ConnectionString { get; init; }
public string? DataSource { get; init; }
public HttpClient? HttpClient { get; init; }
public CSharpDbGrpcMode GrpcMode { get; init; } = CSharpDbGrpcMode.Auto;
public CSharpDbGrpcCallMode GrpcCallMode { get; init; } = CSharpDbGrpcCallMode.Unary;
public string? ApiKey { get; init; }
public string ApiKeyHeaderName { get; init; } = "X-CSharpDB-Api-Key";
public CSharpDbRouteContext? RouteContext { get; init; }
Expand Down
10 changes: 10 additions & 0 deletions src/CSharpDB.Client/CSharpDbGrpcCallMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace CSharpDB.Client;

/// <summary>Selects how SQL and transaction commands travel over native gRPC.</summary>
public enum CSharpDbGrpcCallMode
{
Unary = 0,

/// <summary>Experimental persistent command stream; requires GrpcMode.Native.</summary>
Streaming = 1,
}
17 changes: 17 additions & 0 deletions src/CSharpDB.Client/CSharpDbGrpcMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace CSharpDB.Client;

/// <summary>Controls the HTTP protocol used by the gRPC transport.</summary>
public enum CSharpDbGrpcMode
{
/// <summary>
/// Preserves compatibility: plain HTTP uses gRPC-Web over HTTP/1.1 unless
/// a custom HttpClient is supplied; HTTPS uses native gRPC.
/// </summary>
Auto = 0,

/// <summary>
/// Uses native gRPC and requires HTTP/2, including on a plain HTTP endpoint.
/// An unencrypted server endpoint must be configured for HTTP/2 only.
/// </summary>
Native = 1,
}
Loading
Loading