From d7aa5dfcb00a552f054f7ca530747fbb2e2b603f Mon Sep 17 00:00:00 2001 From: Ahmed Yasin Koculu Date: Tue, 16 Aug 2022 23:31:44 +0200 Subject: [PATCH] Adds read-only mode. --- src/ZoneTree/Collections/BTree/BTree.cs | 14 +++++++------ src/ZoneTree/Core/ZoneTree.cs | 21 ++++++++++++++++++- .../Exceptions/BTreeIsReadOnlyException.cs | 2 +- .../Exceptions/ZoneTreeIsReadOnlyException.cs | 9 ++++++++ src/ZoneTree/ITransactionalZoneTree.cs | 5 +++++ src/ZoneTree/IZoneTree.cs | 5 +++++ .../Transactional/OptimisticZoneTree.cs | 16 +++++++++++++- 7 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 src/ZoneTree/Exceptions/ZoneTreeIsReadOnlyException.cs diff --git a/src/ZoneTree/Collections/BTree/BTree.cs b/src/ZoneTree/Collections/BTree/BTree.cs index e5ba408..1b19567 100644 --- a/src/ZoneTree/Collections/BTree/BTree.cs +++ b/src/ZoneTree/Collections/BTree/BTree.cs @@ -24,16 +24,18 @@ public partial class BTree volatile LeafNode LastLeafNode; public readonly IRefComparer Comparer; - + volatile int _length; public int Length => _length; - + public readonly BTreeLockMode LockMode; public IIncrementalIdProvider OpIndexProvider { get; } - public bool IsReadOnly { get; set; } + volatile bool _isReadOnly; + + public bool IsReadOnly { get => _isReadOnly; set => _isReadOnly = value; } public BTree( IRefComparer comparer, @@ -62,7 +64,7 @@ public partial class BTree ILocker GetNodeLocker() => LockMode switch { BTreeLockMode.TopLevelMonitor or - BTreeLockMode.TopLevelReaderWriter or + BTreeLockMode.TopLevelReaderWriter or BTreeLockMode.NoLock => new NoLock(), BTreeLockMode.NodeLevelMonitor => new MonitorLock(), @@ -129,7 +131,7 @@ public partial class BTree var iterator = GetFrozenLeafNode(key).GetFrozenIterator(); return iterator.SeekFirstKeyGreaterOrEqual(Comparer, in key); } - + public NodeIterator GetFirstIterator() { try @@ -166,7 +168,7 @@ public partial class BTree return LastLeafNode.GetFrozenIterator(); } - public void SetNextOpIndex(long nextId) + public void SetNextOpIndex(long nextId) => OpIndexProvider.SetNextId(nextId); public long GetLastOpIndex() diff --git a/src/ZoneTree/Core/ZoneTree.cs b/src/ZoneTree/Core/ZoneTree.cs index 8dc89c4..d0a18b5 100644 --- a/src/ZoneTree/Core/ZoneTree.cs +++ b/src/ZoneTree/Core/ZoneTree.cs @@ -1,8 +1,8 @@ #undef TRACE_MERGE using System.Collections.Concurrent; -using System.Diagnostics; using Tenray.ZoneTree.Collections; +using Tenray.ZoneTree.Exceptions; using Tenray.ZoneTree.Segments; using Tenray.ZoneTree.Segments.Disk; @@ -95,6 +95,10 @@ public sealed class ZoneTree : IZoneTree, IZoneTreeM public event ZoneTreeIsDisposing OnZoneTreeIsDisposing; + volatile bool _isReadOnly; + + public bool IsReadOnly { get => _isReadOnly; set => _isReadOnly = value; } + public ZoneTree(ZoneTreeOptions options) { options.WriteAheadLogProvider.InitCategory(SegmentWalCategory); @@ -207,6 +211,9 @@ public sealed class ZoneTree : IZoneTree, IZoneTreeM public bool TryAtomicAdd(in TKey key, in TValue value) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); + if (key == null) throw new ArgumentNullException(nameof(key)); lock (AtomicUpdateLock) @@ -220,6 +227,8 @@ public sealed class ZoneTree : IZoneTree, IZoneTreeM public bool TryAtomicUpdate(in TKey key, in TValue value) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); if (key == null) throw new ArgumentNullException(nameof(key)); lock (AtomicUpdateLock) @@ -233,6 +242,8 @@ public sealed class ZoneTree : IZoneTree, IZoneTreeM public bool TryAtomicAddOrUpdate(in TKey key, in TValue valueToAdd, Func valueToUpdateGetter) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); if (key == null) throw new ArgumentNullException(nameof(key)); AddOrUpdateResult status; @@ -276,6 +287,8 @@ public sealed class ZoneTree : IZoneTree, IZoneTreeM public void AtomicUpsert(in TKey key, in TValue value) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); lock (AtomicUpdateLock) { Upsert(in key, in value); @@ -284,6 +297,8 @@ public sealed class ZoneTree : IZoneTree, IZoneTreeM public void Upsert(in TKey key, in TValue value) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); if (key == null) throw new ArgumentNullException(nameof(key)); while (true) @@ -305,6 +320,8 @@ public sealed class ZoneTree : IZoneTree, IZoneTreeM public bool TryDelete(in TKey key) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); if (key == null) throw new ArgumentNullException(nameof(key)); if (!ContainsKey(key)) @@ -315,6 +332,8 @@ public sealed class ZoneTree : IZoneTree, IZoneTreeM public void ForceDelete(in TKey key) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); if (key == null) throw new ArgumentNullException(nameof(key)); while (true) diff --git a/src/ZoneTree/Exceptions/BTreeIsReadOnlyException.cs b/src/ZoneTree/Exceptions/BTreeIsReadOnlyException.cs index b544e8b..fc2d2dc 100644 --- a/src/ZoneTree/Exceptions/BTreeIsReadOnlyException.cs +++ b/src/ZoneTree/Exceptions/BTreeIsReadOnlyException.cs @@ -6,4 +6,4 @@ public class BTreeIsReadOnlyException : ZoneTreeException : base("Can't write to read-only BTree.") { } -} \ No newline at end of file +} diff --git a/src/ZoneTree/Exceptions/ZoneTreeIsReadOnlyException.cs b/src/ZoneTree/Exceptions/ZoneTreeIsReadOnlyException.cs new file mode 100644 index 0000000..a83d76b --- /dev/null +++ b/src/ZoneTree/Exceptions/ZoneTreeIsReadOnlyException.cs @@ -0,0 +1,9 @@ +namespace Tenray.ZoneTree.Exceptions; + +public class ZoneTreeIsReadOnlyException : ZoneTreeException +{ + public ZoneTreeIsReadOnlyException() + : base("Can't write to read-only ZoneTree.") + { + } +} \ No newline at end of file diff --git a/src/ZoneTree/ITransactionalZoneTree.cs b/src/ZoneTree/ITransactionalZoneTree.cs index 9738572..da7e864 100644 --- a/src/ZoneTree/ITransactionalZoneTree.cs +++ b/src/ZoneTree/ITransactionalZoneTree.cs @@ -206,4 +206,9 @@ public interface ITransactionalZoneTree : IDisposable /// /// Key void DeleteAutoCommit(in TKey key); + + /// + /// Enables read-only mode. + /// + public bool IsReadOnly { get; set; } } diff --git a/src/ZoneTree/IZoneTree.cs b/src/ZoneTree/IZoneTree.cs index 1bb310b..d261e03 100644 --- a/src/ZoneTree/IZoneTree.cs +++ b/src/ZoneTree/IZoneTree.cs @@ -161,4 +161,9 @@ public interface IZoneTree : IDisposable /// Returns maintenance object belongs to this ZoneTree. /// IZoneTreeMaintenance Maintenance { get; } + + /// + /// Enables read-only mode. + /// + public bool IsReadOnly { get; set; } } diff --git a/src/ZoneTree/Transactional/OptimisticZoneTree.cs b/src/ZoneTree/Transactional/OptimisticZoneTree.cs index 4a669f6..e10bb3c 100644 --- a/src/ZoneTree/Transactional/OptimisticZoneTree.cs +++ b/src/ZoneTree/Transactional/OptimisticZoneTree.cs @@ -21,6 +21,12 @@ public sealed class OptimisticZoneTree : public IReadOnlyList UncommittedTransactionIds => TransactionLog.UncommittedTransactionIds; + public bool IsReadOnly + { + get => ZoneTree.IsReadOnly; + set => ZoneTree.IsReadOnly = value; + } + public OptimisticZoneTree( ZoneTreeOptions options, ITransactionLog transactionLog, @@ -381,6 +387,8 @@ public sealed class OptimisticZoneTree : public void UpsertAutoCommit(in TKey key, in TValue oldVal) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); lock (this) { var transactionId = BeginTransaction(); @@ -406,6 +414,8 @@ public sealed class OptimisticZoneTree : public void DeleteAutoCommit(in TKey key) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); lock (this) { var transactionId = BeginTransaction(); @@ -498,6 +508,8 @@ public sealed class OptimisticZoneTree : public TransactionResult UpsertNoThrow(long transactionId, in TKey key, in TValue value) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); lock (this) { var transaction = GetOrCreateTransactionNoAbortThrow(transactionId); @@ -528,11 +540,13 @@ public sealed class OptimisticZoneTree : TransactionLog.AddOrUpdateReadWriteStamp(key, in readWriteStamp); ZoneTree.Upsert(in key, in value); return TransactionResult.Success(!hasOldValue); - } + } } public TransactionResult DeleteNoThrow(long transactionId, in TKey key) { + if (IsReadOnly) + throw new ZoneTreeIsReadOnlyException(); lock (this) { var transaction = GetOrCreateTransactionNoAbortThrow(transactionId);