Skip to content

Commit d1c1931

Browse files
author
MPCoreDeveloper
committed
fix(config): Auto engine selection must not land on PageBased (hardening)
With default config (StorageEngineType.Auto + WorkloadHint.General), GetOptimalStorageEngine returned PageBased. The PageBased engine is not yet OLTP-ready (measured UPDATE ~26K ops/s vs ~245K ops/s on the fixed-width Columnar path), so every default-created database risked ending up on the slow engine wherever Auto selection is honored. Auto now routes General / WriteHeavy / unknown hints to AppendOnly/Columnar; PageBased stays reachable only through an explicit StorageEngineType.PageBased until its UPDATE/DELETE fast paths reach parity. Regression: DefaultEngineSelectionTests assert (1) the mapping itself and (2) that a DEFAULT database creates a Columnar fixed-width PK table that engages the single-pass contiguous DELETE fast path and leaves no .pages artifacts. Full suite 1768 tests, 0 failed.
1 parent 31ac270 commit d1c1931

3 files changed

Lines changed: 121 additions & 7 deletions

File tree

docs/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Hardening
11+
12+
- **Auto engine selection no longer lands on PageBased (production hardening)** - with default
13+
configuration (`StorageEngineType.Auto` + `WorkloadHint.General`) `GetOptimalStorageEngine`
14+
returned PageBased, which is not yet OLTP-ready (measured UPDATE ~26K ops/s vs ~245K ops/s on the
15+
fixed-width Columnar path). Auto selection now routes General / WriteHeavy / unknown hints to
16+
AppendOnly/Columnar; PageBased remains reachable only through an explicit
17+
`StorageEngineType.PageBased` until its UPDATE/DELETE fast paths reach parity. Regression tests
18+
assert the mapping AND that a default database creates Columnar fixed-width PK tables that engage
19+
the single-pass contiguous DELETE path (no `.pages` artifacts). Full suite 1768 tests, 0 failed.
20+
1021
### Performance
1122

1223
- **Fixed-width record layout is now the default for new columnar PK tables (B7)** ÔÇö

src/SharpCoreDB/DatabaseConfig.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -563,9 +563,15 @@ public class DatabaseConfig
563563
/// ✅ NEW: Smart storage selection based on workload characteristics!
564564
/// - ReadHeavy: Optimized for SELECT queries → COLUMNAR storage
565565
/// - Analytics: Optimized for aggregates/scans → COLUMNAR storage
566-
/// - WriteHeavy: Optimized for INSERT/UPDATE → PAGE_BASED storage
567-
/// - General: Balanced for mixed workloads → PAGE_BASED storage
568-
///
566+
/// - WriteHeavy: Optimized for INSERT/UPDATE → currently APPEND_ONLY/COLUMNAR too (PageBased is
567+
/// opt-in only via an explicit <see cref="StorageEngineType.PageBased"/>; see the note below)
568+
/// - General: Balanced for mixed workloads → APPEND_ONLY/COLUMNAR storage (the fast, hardened path)
569+
///
570+
/// NOTE (production hardening): the PageBased engine is NOT yet OLTP-ready — measured UPDATE is
571+
/// ~26K ops/s vs ~245K ops/s on the fixed-width Columnar (AppendOnly) path. Auto selection
572+
/// therefore routes the default General workload (and the unknown-hint fallback) to
573+
/// AppendOnly/Columnar until PageBased reaches UPDATE/DELETE parity.
574+
///
569575
/// When StorageEngineType = Auto, the engine is selected based on this hint.
570576
/// </summary>
571577
public WorkloadHint WorkloadHint { get; init; } = WorkloadHint.General;
@@ -583,14 +589,15 @@ public Interfaces.StorageEngineType GetOptimalStorageEngine()
583589
return StorageEngineType;
584590
}
585591

586-
// Auto-select based on workload hint
592+
// Auto-select based on workload hint. PageBased is deliberately not selected for General /
593+
// unknown hints (and should be treated as opt-in only) until its UPDATE/DELETE fast paths
594+
// reach the fixed-width Columnar engine's parity (see class-level note).
587595
return WorkloadHint switch
588596
{
589597
WorkloadHint.ReadHeavy => Interfaces.StorageEngineType.Columnar,
590598
WorkloadHint.Analytics => Interfaces.StorageEngineType.Columnar,
591-
WorkloadHint.WriteHeavy => Interfaces.StorageEngineType.PageBased,
592-
WorkloadHint.General => Interfaces.StorageEngineType.PageBased,
593-
_ => Interfaces.StorageEngineType.PageBased // Default to PAGE_BASED (safest choice)
599+
WorkloadHint.General => Interfaces.StorageEngineType.AppendOnly,
600+
_ => Interfaces.StorageEngineType.AppendOnly
594601
};
595602
}
596603

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// <copyright file="DefaultEngineSelectionTests.cs" company="MPCoreDeveloper">
2+
// Copyright (c) 2026 MPCoreDeveloper. All rights reserved.
3+
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
4+
// </copyright>
5+
namespace SharpCoreDB.Tests;
6+
7+
using Microsoft.Extensions.DependencyInjection;
8+
using SharpCoreDB.DataStructures;
9+
using SharpCoreDB.Interfaces;
10+
using SharpCoreDB.Storage.Hybrid;
11+
using System;
12+
using System.Collections.Generic;
13+
using System.IO;
14+
using System.Linq;
15+
using Xunit;
16+
17+
/// <summary>
18+
/// Production-hardening regression: a database created with DEFAULT configuration must stay on the
19+
/// fast, hardened path — Columnar (AppendOnly) tables with the fixed-width record layout for PK
20+
/// tables and the single-pass contiguous DELETE fast path — and must never silently land on the
21+
/// not-yet-OLTP-ready PageBased engine through the Auto/WorkloadHint selection.
22+
/// </summary>
23+
public sealed class DefaultEngineSelectionTests : IDisposable
24+
{
25+
private readonly DatabaseFactory _factory;
26+
private readonly string _dirPath;
27+
28+
public DefaultEngineSelectionTests()
29+
{
30+
var services = new ServiceCollection();
31+
services.AddSharpCoreDB();
32+
_factory = services.BuildServiceProvider().GetRequiredService<DatabaseFactory>();
33+
_dirPath = Path.Combine(Path.GetTempPath(), $"SCDB_DefaultEng_{Guid.NewGuid():N}");
34+
}
35+
36+
public void Dispose()
37+
{
38+
try { if (Directory.Exists(_dirPath)) Directory.Delete(_dirPath, true); } catch { }
39+
}
40+
41+
[Fact]
42+
public void DefaultConfig_AutoSelection_PrefersAppendOnlyOverPageBased()
43+
{
44+
// Regression: WorkloadHint.General (the default) and unknown hints used to resolve Auto to
45+
// PageBased, which is not OLTP-ready (measured UPDATE ~26K ops/s vs ~245K on the
46+
// fixed-width Columnar path). Explicit PageBased opt-in must remain possible.
47+
var config = new DatabaseConfig();
48+
49+
Assert.Equal(StorageEngineType.Auto, config.StorageEngineType);
50+
Assert.Equal(StorageEngineType.AppendOnly, config.GetOptimalStorageEngine());
51+
52+
var explicitPageBased = new DatabaseConfig { StorageEngineType = StorageEngineType.PageBased };
53+
Assert.Equal(StorageEngineType.PageBased, explicitPageBased.GetOptimalStorageEngine());
54+
}
55+
56+
[Fact]
57+
public void DefaultDatabase_NewPkTable_UsesColumnarFixedWidthAndContiguousDelete()
58+
{
59+
var db = _factory.Create(_dirPath, "pw", isReadOnly: false, config: new DatabaseConfig());
60+
try
61+
{
62+
db.ExecuteSQL("CREATE TABLE docs (id INTEGER PRIMARY KEY, name TEXT, score REAL)");
63+
Assert.True(db.TryGetTable("docs", out var t));
64+
var table = Assert.IsType<Table>(t);
65+
66+
Assert.Equal(StorageMode.Columnar, table.StorageMode);
67+
Assert.True(table.IsFixedWidthRecords, "default new PK table must use the fixed-width record layout");
68+
Assert.Equal(StorageEngineType.AppendOnly, table.GetStorageEngineType());
69+
70+
var stmts = new List<string>(1000);
71+
for (int i = 1; i <= 1000; i++)
72+
{
73+
stmts.Add($"INSERT INTO docs VALUES ({i}, 'user{i}', {i * 0.5})");
74+
}
75+
76+
db.ExecuteBatchSQL(stmts);
77+
db.Flush();
78+
79+
var dels = new List<string>(500);
80+
for (int i = 1; i <= 500; i++)
81+
{
82+
dels.Add($"DELETE FROM docs WHERE id = {i}");
83+
}
84+
85+
db.ExecuteBatchSQL(dels);
86+
db.Flush();
87+
88+
Assert.Equal(1, table.BulkContiguousDeleteBatches); // fast path engaged on default config
89+
Assert.Equal(500, db.ExecuteQuery("SELECT id FROM docs").Count);
90+
91+
// No PageBased .pages artifact may appear for a default Columnar table.
92+
Assert.False(Directory.EnumerateFiles(_dirPath, "*.pages").Any());
93+
}
94+
finally { (db as IDisposable)?.Dispose(); }
95+
}
96+
}

0 commit comments

Comments
 (0)