Add enterprise docs structure and include pending core maintenance updates.

This commit is contained in:
Joseph Doherty
2026-02-20 13:28:29 -05:00
parent b8ed5ec500
commit 52445078a1
23 changed files with 1956 additions and 404 deletions

View File

@@ -124,14 +124,28 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
_indexManager.SetPrimaryRootPageId(_primaryIndex.RootPageId);
}
// Register keys used by the mapper to ensure they are available for compression
_storage.RegisterKeys(_mapper.UsedKeys);
}
private void EnsureSchema()
{
var currentSchema = _mapper.GetSchema();
var metadata = _indexManager.GetMetadata();
// Register keys used by the mapper to ensure they are available for compression
_storage.RegisterKeys(_mapper.UsedKeys);
}
private void RefreshPrimaryIndexRootFromMetadata()
{
_indexManager.RefreshFromStorageMetadata();
var primaryRootPageId = _indexManager.PrimaryRootPageId;
if (primaryRootPageId == 0)
return;
if (primaryRootPageId != _primaryIndex.RootPageId)
{
_primaryIndex.SetRootPageId(primaryRootPageId);
}
}
private void EnsureSchema()
{
var currentSchema = _mapper.GetSchema();
var metadata = _indexManager.GetMetadata();
var persistedSchemas = _storage.GetSchemas(metadata.SchemaRootPageId);
var latestPersisted = persistedSchemas.Count > 0 ? persistedSchemas[persistedSchemas.Count - 1] : null;
@@ -363,12 +377,13 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
/// Rebuilds an index by scanning all existing documents and re-inserting them.
/// Called automatically when creating a new index.
/// </summary>
private void RebuildIndex(CollectionSecondaryIndex<TId, T> index)
{
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
// Iterate all documents in the collection via primary index
var minKey = new IndexKey(Array.Empty<byte>());
var maxKey = new IndexKey(Enumerable.Repeat((byte)0xFF, 32).ToArray());
private void RebuildIndex(CollectionSecondaryIndex<TId, T> index)
{
RefreshPrimaryIndexRootFromMetadata();
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
// Iterate all documents in the collection via primary index
var minKey = new IndexKey(Array.Empty<byte>());
var maxKey = new IndexKey(Enumerable.Repeat((byte)0xFF, 32).ToArray());
foreach (var entry in _primaryIndex.Range(minKey, maxKey, IndexDirection.Forward, transaction.TransactionId))
{
@@ -967,6 +982,7 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
private void InsertDataCore(TId id, T entity, ReadOnlySpan<byte> docData)
{
RefreshPrimaryIndexRootFromMetadata();
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
var (storedPayloadOverride, storedPayloadFlags) = PreparePayloadForStorage(docData);
ReadOnlySpan<byte> storedPayload = storedPayloadOverride is null ? docData : storedPayloadOverride;
@@ -1005,11 +1021,12 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
/// <param name="id">ObjectId of the document</param>
/// <param name="transaction">Optional transaction for isolation (supports Read Your Own Writes)</param>
/// <returns>The document, or null if not found</returns>
public T? FindById(TId id)
{
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
try
{
public T? FindById(TId id)
{
RefreshPrimaryIndexRootFromMetadata();
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
try
{
var key = _mapper.ToIndexKey(id);
if (!_primaryIndex.TryFind(key, out var location, transaction.TransactionId))
@@ -1031,11 +1048,12 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
/// </summary>
/// <param name="transaction">Transaction for isolation (REQUIRED for consistent reads during concurrent writes)</param>
/// <returns>Enumerable of all documents</returns>
public IEnumerable<T> FindAll()
{
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
var txnId = transaction?.TransactionId ?? 0;
var minKey = new IndexKey(Array.Empty<byte>());
public IEnumerable<T> FindAll()
{
RefreshPrimaryIndexRootFromMetadata();
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
var txnId = transaction?.TransactionId ?? 0;
var minKey = new IndexKey(Array.Empty<byte>());
var maxKey = new IndexKey(Enumerable.Repeat((byte)0xFF, 32).ToArray());
foreach (var entry in _primaryIndex.Range(minKey, maxKey, IndexDirection.Forward, txnId))
@@ -1202,11 +1220,12 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
}
}
private int UpdateBulkInternal(List<T> entityList)
{
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
int updateCount = 0;
const int BATCH_SIZE = 50;
private int UpdateBulkInternal(List<T> entityList)
{
RefreshPrimaryIndexRootFromMetadata();
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
int updateCount = 0;
const int BATCH_SIZE = 50;
for (int batchStart = 0; batchStart < entityList.Count; batchStart += BATCH_SIZE)
{
@@ -1272,6 +1291,7 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
private bool UpdateDataCore(TId id, T entity, ReadOnlySpan<byte> docData)
{
RefreshPrimaryIndexRootFromMetadata();
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
var key = _mapper.ToIndexKey(id);
var (storedPayloadOverride, storedPayloadFlags) = PreparePayloadForStorage(docData);
@@ -1438,11 +1458,12 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
return deleteCount;
}
private bool DeleteCore(TId id, bool notifyCdc = true)
{
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
var key = _mapper.ToIndexKey(id);
if (!_primaryIndex.TryFind(key, out var location, transaction.TransactionId))
private bool DeleteCore(TId id, bool notifyCdc = true)
{
RefreshPrimaryIndexRootFromMetadata();
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
var key = _mapper.ToIndexKey(id);
if (!_primaryIndex.TryFind(key, out var location, transaction.TransactionId))
return false;
// Notify secondary indexes BEFORE deleting document from storage
@@ -1524,11 +1545,12 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
/// </summary>
/// <param name="transaction">Optional transaction for isolation</param>
/// <returns>Number of documents</returns>
public int Count()
{
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
// Count all entries in primary index
// Use generic min/max keys for the index
public int Count()
{
RefreshPrimaryIndexRootFromMetadata();
var transaction = _transactionHolder.GetCurrentTransactionOrStart();
// Count all entries in primary index
// Use generic min/max keys for the index
var minKey = IndexKey.MinKey;
var maxKey = IndexKey.MaxKey;
return _primaryIndex.Range(minKey, maxKey, IndexDirection.Forward, transaction.TransactionId).Count();