Skip to content

Commit 020228a

Browse files
authored
Resolve separate DBreeze CoinDb implementation (stratisproject#1036)
* Resolve separate DBreeze CoinDb implementation * Add tables * Changes based on feedback
1 parent 63e391d commit 020228a

File tree

8 files changed

+248
-381
lines changed

8 files changed

+248
-381
lines changed

src/Stratis.Bitcoin.Features.Consensus.Tests/CoinViews/CoinviewTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public CoinviewTests()
4242
this.loggerFactory = new ExtendedLoggerFactory();
4343
this.nodeStats = new NodeStats(this.dateTimeProvider, NodeSettings.Default(this.network), new Mock<IVersionProvider>().Object);
4444

45-
this.coindb = new DBreezeCoindb(this.network, this.dataFolder, this.dateTimeProvider, this.loggerFactory, this.nodeStats, new DBreezeSerializer(this.network.Consensus.ConsensusFactory));
45+
this.coindb = new Coindb<DBreezeDbWithCoinDbNames>(this.network, this.dataFolder, this.dateTimeProvider, this.nodeStats, new DBreezeSerializer(this.network.Consensus.ConsensusFactory));
4646
this.coindb.Initialize();
4747

4848
this.chainIndexer = new ChainIndexer(this.network);

src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/Coindb.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public void SaveChanges(IList<UnspentOutput> unspentOutputs, HashHeightPair oldB
213213
{
214214
int insertedEntities = 0;
215215

216-
using (var batch = this.coinDb.GetWriteBatch())
216+
using (var batch = this.coinDb.GetWriteBatch(coinsTable, rewindTable, blockTable))
217217
{
218218
using (new StopwatchDisposable(o => this.performanceCounter.AddInsertTime(o)))
219219
{
@@ -300,7 +300,7 @@ private HashHeightPair RewindInternal(int startHeight, HashHeightPair target)
300300
{
301301
HashHeightPair res = null;
302302

303-
using (var batch = this.coinDb.GetWriteBatch())
303+
using (var batch = this.coinDb.GetWriteBatch(coinsTable, rewindTable, blockTable))
304304
{
305305
for (int height = startHeight; height > (target?.Height ?? (startHeight - 1)) && height > (startHeight - MaxRewindBatchSize); height--)
306306
{

src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/DBreezeCoindb.cs

Lines changed: 9 additions & 374 deletions
Large diffs are not rendered by default.

src/Stratis.Bitcoin.Features.Consensus/FullNodeBuilderConsensusExtension.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public static void ConfigureCoinDatabaseImplementation(this IServiceCollection s
8484
switch (coindbType)
8585
{
8686
case DbType.Dbreeze:
87-
services.AddSingleton<ICoindb, DBreezeCoindb>();
87+
services.AddSingleton<ICoindb, Coindb<DBreezeDbWithCoinDbNames>>();
8888
break;
8989

9090
case DbType.Leveldb:
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using DBreeze;
5+
using DBreeze.DataTypes;
6+
using DBreeze.Transactions;
7+
8+
namespace Stratis.Bitcoin.Database
9+
{
10+
public class BatchContext : IDisposable
11+
{
12+
public Transaction transaction { get; private set; }
13+
14+
private bool canDispose;
15+
16+
public BatchContext(Transaction transaction, bool canDispose)
17+
{
18+
this.transaction = transaction;
19+
this.canDispose = canDispose;
20+
}
21+
22+
public void Dispose()
23+
{
24+
if (this.canDispose)
25+
this.transaction.Dispose();
26+
}
27+
}
28+
29+
/// <summary>A minimal DBreeze wrapper that makes it compliant with the <see cref="IDb"/> interface.</summary>
30+
public class DBreezeDb : IDb
31+
{
32+
private Dictionary<int, Transaction> transactions = new Dictionary<int, Transaction>();
33+
34+
private string dbPath;
35+
36+
private DBreezeEngine db;
37+
38+
private Dictionary<byte, string> tableNames;
39+
40+
public DBreezeDb(Dictionary<byte, string> tableNames)
41+
{
42+
this.tableNames = tableNames;
43+
}
44+
45+
public IDbIterator GetIterator(byte table)
46+
{
47+
return new DBreezeIterator(this, this.tableNames[table]);
48+
}
49+
50+
public IDbIterator GetIterator()
51+
{
52+
return new DBreezeIterator(this, "default");
53+
}
54+
55+
public string GetTableName(byte table)
56+
{
57+
return this.tableNames[table];
58+
}
59+
60+
public void Open(string dbPath)
61+
{
62+
this.dbPath = dbPath;
63+
this.db = new DBreezeEngine(dbPath);
64+
}
65+
66+
public void Clear()
67+
{
68+
this.db.Dispose();
69+
System.IO.Directory.Delete(this.dbPath, true);
70+
this.db = new DBreezeEngine(this.dbPath);
71+
}
72+
73+
public IDbBatch GetWriteBatch(params byte[] tables) => new DBreezeBatch(this, tables);
74+
75+
private (Transaction transaction, bool canDispose) GetTransaction()
76+
{
77+
int threadId = AppDomain.GetCurrentThreadId();
78+
79+
// DBreeze does not allow nested transactions on the same thread.
80+
// Re-use any existing transaction.
81+
if (this.transactions.TryGetValue(threadId, out Transaction currentTransaction))
82+
{
83+
var disposedField = currentTransaction.GetType().GetField("disposed", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
84+
if (!(bool)disposedField.GetValue(currentTransaction))
85+
return (currentTransaction, false);
86+
this.transactions.Remove(threadId);
87+
}
88+
89+
var transaction = this.db.GetTransaction(eTransactionTablesLockTypes.EXCLUSIVE);
90+
91+
this.transactions[threadId] = transaction;
92+
93+
return (transaction, true);
94+
}
95+
96+
public BatchContext GetBatchContext(params byte[] tables)
97+
{
98+
(Transaction transaction, bool canDispose) = this.GetTransaction();
99+
100+
if (tables.Length != 0)
101+
transaction.SynchronizeTables(tables.Select(t => this.tableNames[t]).ToArray());
102+
103+
return new BatchContext(transaction, canDispose);
104+
}
105+
106+
public byte[] Get(byte table, byte[] key)
107+
{
108+
using (BatchContext ctx = this.GetBatchContext())
109+
{
110+
return ctx.transaction.Select<byte[], byte[]>(this.tableNames[table], key)?.Value;
111+
}
112+
}
113+
114+
public byte[] Get(byte[] key)
115+
{
116+
using (BatchContext ctx = this.GetBatchContext())
117+
{
118+
return ctx.transaction.Select<byte[], byte[]>("default", key)?.Value;
119+
}
120+
}
121+
122+
public void Dispose()
123+
{
124+
this.db.Dispose();
125+
}
126+
}
127+
128+
/// <summary>A minimal LevelDb wrapper that makes it compliant with the <see cref="IDbBatch"/> interface.</summary>
129+
public class DBreezeBatch : IDbBatch
130+
{
131+
private DBreezeDb db;
132+
private BatchContext context;
133+
134+
public DBreezeBatch(DBreezeDb db, params byte[] tables) : base()
135+
{
136+
this.db = db;
137+
this.context = db.GetBatchContext(tables);
138+
}
139+
140+
// Methods when using tables.
141+
142+
public IDbBatch Put(byte table, byte[] key, byte[] value)
143+
{
144+
this.context.transaction.Insert(this.db.GetTableName(table), key, value);
145+
return this;
146+
}
147+
148+
public IDbBatch Delete(byte table, byte[] key)
149+
{
150+
this.context.transaction.RemoveKey(this.db.GetTableName(table), key);
151+
return this;
152+
}
153+
154+
// Table-less operations.
155+
156+
public IDbBatch Put(byte[] key, byte[] value)
157+
{
158+
this.context.transaction.Insert("default", key, value);
159+
return this;
160+
}
161+
162+
public IDbBatch Delete(byte[] key)
163+
{
164+
this.context.transaction.RemoveKey("default", key);
165+
return this;
166+
}
167+
168+
public void Write()
169+
{
170+
this.context.transaction.Commit();
171+
}
172+
173+
public void Dispose()
174+
{
175+
this.context.Dispose();
176+
}
177+
}
178+
179+
/// <summary>A minimal LevelDb wrapper that makes it compliant with the <see cref="IDbIterator"/> interface.</summary>
180+
public class DBreezeIterator : IDbIterator
181+
{
182+
private BatchContext context;
183+
private string tableName;
184+
private Row<byte[], byte[]> current;
185+
186+
internal DBreezeIterator(DBreezeDb db, string tableName)
187+
{
188+
this.context = db.GetBatchContext();
189+
this.tableName = tableName;
190+
}
191+
192+
public void Seek(byte[] key)
193+
{
194+
this.current = this.context.transaction.SelectForwardStartFrom<byte[], byte[]>(this.tableName, key, includeStartFromKey: true, AsReadVisibilityScope: true).FirstOrDefault();
195+
}
196+
197+
public void SeekToLast()
198+
{
199+
this.current = this.context.transaction.SelectBackward<byte[], byte[]>(this.tableName, AsReadVisibilityScope: true).FirstOrDefault();
200+
}
201+
202+
public void Next()
203+
{
204+
this.current = this.context.transaction.SelectForwardStartFrom<byte[], byte[]>(this.tableName, this.current.Key, includeStartFromKey: false, AsReadVisibilityScope: true).FirstOrDefault();
205+
}
206+
207+
public void Prev()
208+
{
209+
this.current = this.context.transaction.SelectBackwardStartFrom<byte[], byte[]>(this.tableName, this.current.Key, includeStartFromKey: false, AsReadVisibilityScope: true).FirstOrDefault();
210+
}
211+
212+
public bool IsValid()
213+
{
214+
return this.current?.Exists ?? false;
215+
}
216+
217+
public byte[] Key()
218+
{
219+
return this.current.Key;
220+
}
221+
222+
public byte[] Value()
223+
{
224+
return this.current.Value;
225+
}
226+
227+
public void Dispose()
228+
{
229+
this.context.Dispose();
230+
}
231+
}
232+
}

src/Stratis.Bitcoin/Database/IDb.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public interface IDb : IDisposable
5151
/// <remarks>The <see cref="IDb.Get"/> method will not reflect these changes until they are committed. Use
5252
/// the <see cref="ReadWriteBatch"/> class if uncommitted changes need to be accessed.</remarks>
5353
/// <returns>See <see cref="IDbBatch"/>.</returns>
54-
IDbBatch GetWriteBatch();
54+
IDbBatch GetWriteBatch(params byte[] tables);
5555

5656
/// <summary>
5757
/// Removes all tables and their contents.

src/Stratis.Bitcoin/Database/LevelDb.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void Clear()
3333
this.db = new DB(new Options() { CreateIfMissing = true }, this.dbPath);
3434
}
3535

36-
public IDbBatch GetWriteBatch() => new LevelDbBatch(this.db);
36+
public IDbBatch GetWriteBatch(params byte[] tables) => new LevelDbBatch(this.db);
3737

3838
public byte[] Get(byte table, byte[] key)
3939
{

src/Stratis.Bitcoin/Database/RocksDb.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void Clear()
3434
this.db = RocksDbSharp.RocksDb.Open(new DbOptions().SetCreateIfMissing(), this.dbPath);
3535
}
3636

37-
public IDbBatch GetWriteBatch() => new RocksDbBatch(this.db);
37+
public IDbBatch GetWriteBatch(params byte[] tables) => new RocksDbBatch(this.db);
3838

3939
public byte[] Get(byte table, byte[] key)
4040
{

0 commit comments

Comments
 (0)