Add Gitea NuGet publish workflow and finalize current storage/index/docs updates.
This commit is contained in:
@@ -16,6 +16,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using ZB.MOM.WW.CBDD.Core;
|
||||
|
||||
[assembly: InternalsVisibleTo("ZB.MOM.WW.CBDD.Tests")]
|
||||
|
||||
@@ -48,7 +49,7 @@ public class DocumentCollection<T> : DocumentCollection<ObjectId, T> where T : c
|
||||
/// </summary>
|
||||
/// <typeparam name="TId">Type of the primary key</typeparam>
|
||||
/// <typeparam name="T">Type of the entity</typeparam>
|
||||
public partial class DocumentCollection<TId, T> : IDisposable where T : class
|
||||
public partial class DocumentCollection<TId, T> : IDisposable, ICompactionAwareCollection where T : class
|
||||
{
|
||||
private readonly ITransactionHolder _transactionHolder;
|
||||
private readonly IStorageEngine _storage;
|
||||
@@ -130,15 +131,27 @@ public partial class DocumentCollection<TId, T> : IDisposable where T : class
|
||||
|
||||
private void RefreshPrimaryIndexRootFromMetadata()
|
||||
{
|
||||
_indexManager.RefreshFromStorageMetadata();
|
||||
|
||||
var primaryRootPageId = _indexManager.PrimaryRootPageId;
|
||||
if (primaryRootPageId == 0)
|
||||
var metadata = _storage.GetCollectionMetadata(_collectionName);
|
||||
if (metadata == null || metadata.PrimaryRootPageId == 0)
|
||||
return;
|
||||
|
||||
if (primaryRootPageId != _primaryIndex.RootPageId)
|
||||
if (metadata.PrimaryRootPageId != _primaryIndex.RootPageId)
|
||||
{
|
||||
_primaryIndex.SetRootPageId(primaryRootPageId);
|
||||
_primaryIndex.SetRootPageId(metadata.PrimaryRootPageId);
|
||||
}
|
||||
}
|
||||
|
||||
void ICompactionAwareCollection.RefreshIndexBindingsAfterCompaction()
|
||||
{
|
||||
var metadata = _storage.GetCollectionMetadata(_collectionName);
|
||||
if (metadata == null)
|
||||
return;
|
||||
|
||||
_indexManager.RebindFromMetadata(metadata);
|
||||
|
||||
if (metadata.PrimaryRootPageId != 0 && metadata.PrimaryRootPageId != _primaryIndex.RootPageId)
|
||||
{
|
||||
_primaryIndex.SetRootPageId(metadata.PrimaryRootPageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,15 @@ using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for database contexts.
|
||||
namespace ZB.MOM.WW.CBDD.Core;
|
||||
|
||||
internal interface ICompactionAwareCollection
|
||||
{
|
||||
void RefreshIndexBindingsAfterCompaction();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for database contexts.
|
||||
/// Inherit and add DocumentCollection{T} properties for your entities.
|
||||
/// Use partial class for Source Generator integration.
|
||||
/// </summary>
|
||||
@@ -103,6 +108,7 @@ public abstract partial class DocumentDbContext : IDisposable, ITransactionHolde
|
||||
|
||||
private readonly IReadOnlyDictionary<Type, object> _model;
|
||||
private readonly List<IDocumentMapper> _registeredMappers = new();
|
||||
private readonly List<ICompactionAwareCollection> _compactionAwareCollections = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the concrete storage engine for advanced scenarios in derived contexts.
|
||||
@@ -155,10 +161,14 @@ public abstract partial class DocumentDbContext : IDisposable, ITransactionHolde
|
||||
customName = builder?.CollectionName;
|
||||
}
|
||||
|
||||
_registeredMappers.Add(mapper);
|
||||
var collection = new DocumentCollection<TId, T>(_storage, this, mapper, customName);
|
||||
|
||||
// Apply configurations from ModelBuilder
|
||||
_registeredMappers.Add(mapper);
|
||||
var collection = new DocumentCollection<TId, T>(_storage, this, mapper, customName);
|
||||
if (collection is ICompactionAwareCollection compactionAwareCollection)
|
||||
{
|
||||
_compactionAwareCollections.Add(compactionAwareCollection);
|
||||
}
|
||||
|
||||
// Apply configurations from ModelBuilder
|
||||
if (builder != null)
|
||||
{
|
||||
foreach (var indexBuilder in builder.Indexes)
|
||||
@@ -335,7 +345,9 @@ public abstract partial class DocumentDbContext : IDisposable, ITransactionHolde
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(DocumentDbContext));
|
||||
|
||||
return Engine.Compact(options);
|
||||
var stats = Engine.Compact(options);
|
||||
RefreshCollectionBindingsAfterCompaction();
|
||||
return stats;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -346,7 +358,7 @@ public abstract partial class DocumentDbContext : IDisposable, ITransactionHolde
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(DocumentDbContext));
|
||||
|
||||
return Engine.CompactAsync(options, ct);
|
||||
return CompactAsyncCore(options, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -357,7 +369,9 @@ public abstract partial class DocumentDbContext : IDisposable, ITransactionHolde
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(DocumentDbContext));
|
||||
|
||||
return Engine.Vacuum(options);
|
||||
var stats = Engine.Vacuum(options);
|
||||
RefreshCollectionBindingsAfterCompaction();
|
||||
return stats;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -368,7 +382,29 @@ public abstract partial class DocumentDbContext : IDisposable, ITransactionHolde
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(DocumentDbContext));
|
||||
|
||||
return Engine.VacuumAsync(options, ct);
|
||||
return VacuumAsyncCore(options, ct);
|
||||
}
|
||||
|
||||
private async Task<CompactionStats> CompactAsyncCore(CompactionOptions? options, CancellationToken ct)
|
||||
{
|
||||
var stats = await Engine.CompactAsync(options, ct);
|
||||
RefreshCollectionBindingsAfterCompaction();
|
||||
return stats;
|
||||
}
|
||||
|
||||
private async Task<CompactionStats> VacuumAsyncCore(CompactionOptions? options, CancellationToken ct)
|
||||
{
|
||||
var stats = await Engine.VacuumAsync(options, ct);
|
||||
RefreshCollectionBindingsAfterCompaction();
|
||||
return stats;
|
||||
}
|
||||
|
||||
private void RefreshCollectionBindingsAfterCompaction()
|
||||
{
|
||||
foreach (var collection in _compactionAwareCollections)
|
||||
{
|
||||
collection.RefreshIndexBindingsAfterCompaction();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -505,26 +505,25 @@ public sealed class CollectionIndexManager<TId, T> : IDisposable where T : class
|
||||
public uint PrimaryRootPageId => _metadata.PrimaryRootPageId;
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes cached metadata and index root bindings from storage.
|
||||
/// Rebinds cached metadata and index instances from persisted metadata.
|
||||
/// </summary>
|
||||
internal void RefreshFromStorageMetadata()
|
||||
internal void RebindFromMetadata(CollectionMetadata metadata)
|
||||
{
|
||||
if (metadata == null)
|
||||
throw new ArgumentNullException(nameof(metadata));
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(CollectionIndexManager<TId, T>));
|
||||
|
||||
var latest = _storage.GetCollectionMetadata(_collectionName) ?? new CollectionMetadata { Name = _collectionName };
|
||||
if (MetadataEquals(_metadata, latest))
|
||||
return;
|
||||
|
||||
foreach (var index in _indexes.Values)
|
||||
{
|
||||
try { index.Dispose(); } catch { /* Best effort */ }
|
||||
}
|
||||
|
||||
_indexes.Clear();
|
||||
_metadata = latest;
|
||||
_metadata = metadata;
|
||||
|
||||
foreach (var idxMeta in _metadata.Indexes)
|
||||
{
|
||||
@@ -562,47 +561,6 @@ public sealed class CollectionIndexManager<TId, T> : IDisposable where T : class
|
||||
UpdateMetadata();
|
||||
_storage.SaveCollectionMetadata(_metadata);
|
||||
}
|
||||
|
||||
private static bool MetadataEquals(CollectionMetadata left, CollectionMetadata right)
|
||||
{
|
||||
if (!string.Equals(left.Name, right.Name, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
if (left.PrimaryRootPageId != right.PrimaryRootPageId ||
|
||||
left.SchemaRootPageId != right.SchemaRootPageId ||
|
||||
left.Indexes.Count != right.Indexes.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < left.Indexes.Count; i++)
|
||||
{
|
||||
var l = left.Indexes[i];
|
||||
var r = right.Indexes[i];
|
||||
if (!string.Equals(l.Name, r.Name, StringComparison.OrdinalIgnoreCase) ||
|
||||
l.RootPageId != r.RootPageId ||
|
||||
l.Type != r.Type ||
|
||||
l.IsUnique != r.IsUnique ||
|
||||
l.Dimensions != r.Dimensions ||
|
||||
l.Metric != r.Metric)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var lPaths = l.PropertyPaths ?? Array.Empty<string>();
|
||||
var rPaths = r.PropertyPaths ?? Array.Empty<string>();
|
||||
if (lPaths.Length != rPaths.Length)
|
||||
return false;
|
||||
|
||||
for (var p = 0; p < lPaths.Length; p++)
|
||||
{
|
||||
if (!string.Equals(lPaths[p], rPaths[p], StringComparison.Ordinal))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases resources used by the index manager.
|
||||
|
||||
@@ -779,17 +779,28 @@ public sealed partial class StorageEngine
|
||||
private CompactionSnapshot CaptureCompactionSnapshot()
|
||||
{
|
||||
var fileSizeBytes = _pageFile.FileLengthBytes;
|
||||
var pageCount = _pageFile.NextPageId;
|
||||
var logicalPageCount = _pageFile.NextPageId;
|
||||
var pageSize = _pageFile.PageSize;
|
||||
var pageCount = (uint)(fileSizeBytes / pageSize);
|
||||
|
||||
var freePages = _pageFile.EnumerateFreePages(includeEmptyPages: true);
|
||||
var freePageCount = freePages.Count;
|
||||
var freePageSet = new HashSet<uint>(freePages);
|
||||
|
||||
if (pageCount > logicalPageCount)
|
||||
{
|
||||
for (var pageId = logicalPageCount; pageId < pageCount; pageId++)
|
||||
{
|
||||
freePageSet.Add(pageId);
|
||||
}
|
||||
}
|
||||
|
||||
var freePageCount = freePageSet.Count;
|
||||
|
||||
long slottedFreeBytes = 0;
|
||||
if (pageCount > 1)
|
||||
if (logicalPageCount > 1)
|
||||
{
|
||||
var pageBuffer = new byte[pageSize];
|
||||
for (uint pageId = 1; pageId < pageCount; pageId++)
|
||||
for (uint pageId = 1; pageId < logicalPageCount; pageId++)
|
||||
{
|
||||
_pageFile.ReadPage(pageId, pageBuffer);
|
||||
if (!TryReadSlottedFreeSpace(pageBuffer, out var availableFreeSpace))
|
||||
@@ -805,7 +816,6 @@ public sealed partial class StorageEngine
|
||||
? 0
|
||||
: (totalFreeBytes * 100d) / fileSizeBytes;
|
||||
|
||||
var freePageSet = new HashSet<uint>(freePages);
|
||||
uint tailReclaimablePages = 0;
|
||||
for (var pageId = pageCount; pageId > 2; pageId--)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user