Skip to content

Commit 23d3405

Browse files
committed
Adding transactionClear
1 parent 434ef4a commit 23d3405

File tree

9 files changed

+183
-69
lines changed

9 files changed

+183
-69
lines changed

src/rocksdb/napi/index.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,10 @@ NAPI_METHOD(dbClear) {
469469
std::string* lte = RangeOption(env, options, "lte");
470470
std::string* gt = RangeOption(env, options, "gt");
471471
std::string* gte = RangeOption(env, options, "gte");
472+
const Snapshot* snapshot = SnapshotProperty(env, options, "snapshot");
473+
const bool sync = BooleanProperty(env, options, "sync", false);
472474
ClearWorker* worker = new ClearWorker(env, database, callback, reverse, limit,
473-
lt, lte, gt, gte);
475+
lt, lte, gt, gte, sync, snapshot);
474476
worker->Queue(env);
475477
NAPI_RETURN_UNDEFINED();
476478
}
@@ -959,6 +961,26 @@ NAPI_METHOD(transactionIteratorInit) {
959961
return iterator_ref;
960962
}
961963

964+
NAPI_METHOD(transactionClear) {
965+
NAPI_ARGV(3);
966+
NAPI_TRANSACTION_CONTEXT();
967+
ASSERT_TRANSACTION_READY(env, transaction);
968+
napi_value options = argv[1];
969+
napi_value callback = argv[2];
970+
const bool reverse = BooleanProperty(env, options, "reverse", false);
971+
const int limit = Int32Property(env, options, "limit", -1);
972+
std::string* lt = RangeOption(env, options, "lt");
973+
std::string* lte = RangeOption(env, options, "lte");
974+
std::string* gt = RangeOption(env, options, "gt");
975+
std::string* gte = RangeOption(env, options, "gte");
976+
const TransactionSnapshot* snapshot =
977+
TransactionSnapshotProperty(env, options, "snapshot");
978+
ClearWorker* worker = new ClearWorker(env, transaction, callback, reverse,
979+
limit, lt, lte, gt, gte, snapshot);
980+
worker->Queue(env);
981+
NAPI_RETURN_UNDEFINED();
982+
}
983+
962984
/**
963985
* All exported functions.
964986
*/
@@ -1005,4 +1027,5 @@ NAPI_INIT() {
10051027
NAPI_EXPORT_FUNCTION(transactionDel);
10061028
NAPI_EXPORT_FUNCTION(transactionSnapshot);
10071029
NAPI_EXPORT_FUNCTION(transactionIteratorInit);
1030+
NAPI_EXPORT_FUNCTION(transactionClear);
10081031
}

src/rocksdb/napi/workers/database_workers.cpp

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
#include "../worker.h"
2020
#include "../database.h"
21-
#include "../iterator.h"
21+
#include "../snapshot.h"
2222
#include "../utils.h"
2323

2424
OpenWorker::OpenWorker(napi_env env, Database* database, napi_value callback,
@@ -186,52 +186,6 @@ DelWorker::~DelWorker() { DisposeSliceBuffer(key_); }
186186

187187
void DelWorker::DoExecute() { SetStatus(database_->Del(options_, key_)); }
188188

189-
ClearWorker::ClearWorker(napi_env env, Database* database, napi_value callback,
190-
const bool reverse, const int limit, std::string* lt,
191-
std::string* lte, std::string* gt, std::string* gte)
192-
: PriorityWorker(env, database, callback, "rocksdb.db.clear") {
193-
iterator_ =
194-
new BaseIterator(database, reverse, lt, lte, gt, gte, limit, false);
195-
writeOptions_ = new rocksdb::WriteOptions();
196-
writeOptions_->sync = false;
197-
}
198-
199-
ClearWorker::~ClearWorker() {
200-
delete iterator_;
201-
delete writeOptions_;
202-
}
203-
204-
void ClearWorker::DoExecute() {
205-
iterator_->SeekToRange();
206-
207-
// TODO: add option
208-
uint32_t hwm = 16 * 1024;
209-
rocksdb::WriteBatch batch;
210-
211-
while (true) {
212-
size_t bytesRead = 0;
213-
214-
while (bytesRead <= hwm && iterator_->Valid() && iterator_->Increment()) {
215-
rocksdb::Slice key = iterator_->CurrentKey();
216-
batch.Delete(key);
217-
bytesRead += key.size();
218-
iterator_->Next();
219-
}
220-
221-
if (!SetStatus(iterator_->Status()) || bytesRead == 0) {
222-
break;
223-
}
224-
225-
if (!SetStatus(database_->WriteBatch(*writeOptions_, &batch))) {
226-
break;
227-
}
228-
229-
batch.Clear();
230-
}
231-
232-
iterator_->Close();
233-
}
234-
235189
ApproximateSizeWorker::ApproximateSizeWorker(napi_env env, Database* database,
236190
napi_value callback,
237191
rocksdb::Slice start,

src/rocksdb/napi/workers/database_workers.h

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include <rocksdb/slice.h>
1414

1515
#include "../worker.h"
16-
#include "../iterator.h"
1716
#include "../database.h"
1817
#include "../snapshot.h"
1918

@@ -125,23 +124,6 @@ struct DelWorker final : public PriorityWorker {
125124
rocksdb::Slice key_;
126125
};
127126

128-
/**
129-
* Worker class for deleting a range from a database.
130-
*/
131-
struct ClearWorker final : public PriorityWorker {
132-
ClearWorker(napi_env env, Database* database, napi_value callback,
133-
const bool reverse, const int limit, std::string* lt,
134-
std::string* lte, std::string* gt, std::string* gte);
135-
136-
~ClearWorker();
137-
138-
void DoExecute() override;
139-
140-
private:
141-
BaseIterator* iterator_;
142-
rocksdb::WriteOptions* writeOptions_;
143-
};
144-
145127
/**
146128
* Worker class for calculating the size of a range.
147129
*/

src/rocksdb/napi/workers/iterator_workers.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <cstddef>
66
#include <cstdint>
7+
#include <cassert>
78

89
#include <node/node_api.h>
910

@@ -79,3 +80,71 @@ void NextWorker::DoFinally(napi_env env) {
7980

8081
BaseWorker::DoFinally(env);
8182
}
83+
84+
ClearWorker::ClearWorker(napi_env env, Database* database, napi_value callback,
85+
const bool reverse, const int limit, std::string* lt,
86+
std::string* lte, std::string* gt, std::string* gte,
87+
const bool sync, const Snapshot* snapshot)
88+
: PriorityWorker(env, database, callback, "rocksdb.db.clear") {
89+
iterator_ = new BaseIterator(database, reverse, lt, lte, gt, gte, limit,
90+
false, snapshot);
91+
writeOptions_ = new rocksdb::WriteOptions();
92+
writeOptions_->sync = sync;
93+
}
94+
95+
ClearWorker::ClearWorker(napi_env env, Transaction* transaction,
96+
napi_value callback, const bool reverse,
97+
const int limit, std::string* lt, std::string* lte,
98+
std::string* gt, std::string* gte,
99+
const TransactionSnapshot* snapshot)
100+
: PriorityWorker(env, transaction, callback, "rocksdb.db.clear") {
101+
iterator_ = new BaseIterator(transaction, reverse, lt, lte, gt, gte, limit,
102+
false, snapshot);
103+
writeOptions_ = nullptr;
104+
}
105+
106+
ClearWorker::~ClearWorker() {
107+
delete iterator_;
108+
delete writeOptions_;
109+
}
110+
111+
void ClearWorker::DoExecute() {
112+
assert(database_ != nullptr || transaction_ != nullptr);
113+
iterator_->SeekToRange();
114+
uint32_t hwm = 16 * 1024;
115+
if (database_ != nullptr) {
116+
rocksdb::WriteBatch batch;
117+
while (true) {
118+
size_t bytesRead = 0;
119+
while (bytesRead <= hwm && iterator_->Valid() && iterator_->Increment()) {
120+
rocksdb::Slice key = iterator_->CurrentKey();
121+
// If this fails, we return
122+
if (!SetStatus(batch.Delete(key))) return;
123+
bytesRead += key.size();
124+
iterator_->Next();
125+
}
126+
if (!SetStatus(iterator_->Status()) || bytesRead == 0) {
127+
break;
128+
}
129+
if (!SetStatus(database_->WriteBatch(*writeOptions_, &batch))) {
130+
break;
131+
}
132+
batch.Clear();
133+
}
134+
} else if (transaction_ != nullptr) {
135+
while (true) {
136+
size_t bytesRead = 0;
137+
while (bytesRead <= hwm && iterator_->Valid() && iterator_->Increment()) {
138+
rocksdb::Slice key = iterator_->CurrentKey();
139+
// If this fails, we return
140+
if (!SetStatus(transaction_->Del(key))) return;
141+
bytesRead += key.size();
142+
iterator_->Next();
143+
}
144+
if (!SetStatus(iterator_->Status()) || bytesRead == 0) {
145+
break;
146+
}
147+
}
148+
}
149+
iterator_->Close();
150+
}

src/rocksdb/napi/workers/iterator_workers.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@
55
#endif
66

77
#include <cstdint>
8+
#include <string>
89

910
#include <node/node_api.h>
11+
#include <rocksdb/options.h>
1012

1113
#include "../worker.h"
14+
#include "../database.h"
1215
#include "../iterator.h"
16+
#include "../transaction.h"
17+
#include "../snapshot.h"
1318

1419
/**
1520
* Worker class for closing an iterator
@@ -47,3 +52,26 @@ struct NextWorker final : public BaseWorker {
4752
uint32_t size_;
4853
bool ok_;
4954
};
55+
56+
/**
57+
* Worker class for deleting a range from a database.
58+
*/
59+
struct ClearWorker final : public PriorityWorker {
60+
ClearWorker(napi_env env, Database* database, napi_value callback,
61+
const bool reverse, const int limit, std::string* lt,
62+
std::string* lte, std::string* gt, std::string* gte,
63+
const bool sync, const Snapshot* snapshot = nullptr);
64+
65+
ClearWorker(napi_env env, Transaction* transaction, napi_value callback,
66+
const bool reverse, const int limit, std::string* lt,
67+
std::string* lte, std::string* gt, std::string* gte,
68+
const TransactionSnapshot* snapshot = nullptr);
69+
70+
~ClearWorker();
71+
72+
void DoExecute() override;
73+
74+
private:
75+
BaseIterator* iterator_;
76+
rocksdb::WriteOptions* writeOptions_;
77+
};

src/rocksdb/rocksdb.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,14 @@ interface RocksDB {
206206
options: RocksDBIteratorOptions<RocksDBTransactionSnapshot> & { valueEncoding: 'buffer' },
207207
): RocksDBIterator<string, Buffer>;
208208
transactionIteratorInit(
209-
database: RocksDBDatabase,
209+
transaction: RocksDBTransaction,
210210
options: RocksDBIteratorOptions<RocksDBTransactionSnapshot>,
211211
): RocksDBIterator<string, string>;
212+
transactionClear(
213+
transaction: RocksDBTransaction,
214+
options: RocksDBClearOptions<RocksDBTransactionSnapshot>,
215+
callback: Callback<[], void>,
216+
): void;
212217
}
213218

214219
const rocksdb: RocksDB = nodeGypBuild(path.join(__dirname, '../../'));

src/rocksdb/rocksdbP.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ interface RocksDBP {
175175
database: RocksDBTransaction,
176176
options: RocksDBIteratorOptions<RocksDBTransactionSnapshot>,
177177
): RocksDBIterator<string, string>;
178+
transactionClear(
179+
transaction: RocksDBTransaction,
180+
options: RocksDBClearOptions<RocksDBTransactionSnapshot>,
181+
): Promise<void>;
178182
}
179183

180184
/**
@@ -217,6 +221,7 @@ const rocksdbP: RocksDBP = {
217221
transactionDel: utils.promisify(rocksdb.transactionDel).bind(rocksdb),
218222
transactionSnapshot: rocksdb.transactionSnapshot.bind(rocksdb),
219223
transactionIteratorInit: rocksdb.transactionIteratorInit.bind(rocksdb),
224+
transactionClear: rocksdb.transactionClear.bind(rocksdb),
220225
};
221226

222227
export default rocksdbP;

src/rocksdb/types.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,21 @@ type RocksDBRangeOptions = {
132132
* Note that `undefined` is not a valid value for these options
133133
* If properties exist, they must have the correct type
134134
*/
135-
type RocksDBClearOptions = RocksDBRangeOptions;
135+
type RocksDBClearOptions
136+
<S extends RocksDBSnapshot | RocksDBTransactionSnapshot = RocksDBSnapshot>
137+
= RocksDBRangeOptions & {
138+
snapshot?: S;
139+
sync?: S extends RocksDBSnapshot ? boolean : void; // Default false
140+
};
136141

137142
/**
138143
* Iterator options
139144
* Note that `undefined` is not a valid value for these options
140145
* If properties exist, they must have the correct type
141146
*/
142-
type RocksDBIteratorOptions<S extends RocksDBSnapshot | RocksDBTransactionSnapshot = RocksDBSnapshot> = RocksDBGetOptions<S> &
147+
type RocksDBIteratorOptions
148+
<S extends RocksDBSnapshot | RocksDBTransactionSnapshot = RocksDBSnapshot>
149+
= RocksDBGetOptions<S> &
143150
RocksDBRangeOptions & {
144151
keys?: boolean;
145152
values?: boolean;

tests/rocksdb/rocksdbP.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,30 @@ describe('rocksdbP', () => {
143143
);
144144
await rocksdbP.iteratorClose(iter);
145145
});
146+
test('dbClear with implicit snapshot', async () => {
147+
await rocksdbP.dbPut(db, 'K1', '100', {});
148+
await rocksdbP.dbPut(db, 'K2', '100', {});
149+
await rocksdbP.dbClear(db, {});
150+
await expect(rocksdbP.dbGet(db, 'K1', {})).rejects.toHaveProperty('code', 'NOT_FOUND');
151+
await expect(rocksdbP.dbGet(db, 'K2', {})).rejects.toHaveProperty('code', 'NOT_FOUND');
152+
});
153+
test('dbClear with explicit snapshot', async () => {
154+
await rocksdbP.dbPut(db, 'K1', '100', {});
155+
await rocksdbP.dbPut(db, 'K2', '100', {});
156+
const snap = rocksdbP.snapshotInit(db);
157+
await rocksdbP.dbPut(db, 'K1', '200', {});
158+
await rocksdbP.dbPut(db, 'K2', '200', {});
159+
await rocksdbP.dbPut(db, 'K3', '200', {});
160+
await rocksdbP.dbPut(db, 'K4', '200', {});
161+
await rocksdbP.dbClear(db, {
162+
snapshot: snap
163+
});
164+
await rocksdbP.snapshotRelease(snap);
165+
await expect(rocksdbP.dbGet(db, 'K1', {})).rejects.toHaveProperty('code', 'NOT_FOUND');
166+
await expect(rocksdbP.dbGet(db, 'K2', {})).rejects.toHaveProperty('code', 'NOT_FOUND');
167+
expect(await rocksdbP.dbGet(db, 'K3', {})).toBe('200');
168+
expect(await rocksdbP.dbGet(db, 'K4', {})).toBe('200');
169+
});
146170
});
147171
describe('transactions', () => {
148172
test('transactionCommit is idempotent', async () => {
@@ -357,6 +381,23 @@ describe('rocksdbP', () => {
357381
// at the beginning of the transaction
358382
await rocksdbP.transactionRollback(tran);
359383
});
384+
test.only('clear with repeatable read', async () => {
385+
await rocksdbP.dbPut(db, 'K1', '100', {});
386+
await rocksdbP.dbPut(db, 'K2', '100', {});
387+
const tran = rocksdbP.transactionInit(db, {});
388+
const tranSnap = rocksdbP.transactionSnapshot(tran);
389+
await rocksdbP.transactionPut(tran, 'K2', '200');
390+
await rocksdbP.transactionPut(tran, 'K3', '200');
391+
await rocksdbP.dbPut(db, 'K4', '200', {});
392+
console.log('OH NO');
393+
await rocksdbP.transactionClear(tran, { snapshot: tranSnap });
394+
console.log('????');
395+
await rocksdbP.transactionCommit(tran);
396+
await expect(rocksdbP.dbGet(db, 'K1', {})).rejects.toHaveProperty('code', 'NOT_FOUND');
397+
await expect(rocksdbP.dbGet(db, 'K2', {})).rejects.toHaveProperty('code', 'NOT_FOUND');
398+
await expect(rocksdbP.dbGet(db, 'K3', {})).rejects.toHaveProperty('code', 'NOT_FOUND');
399+
expect(await rocksdbP.dbGet(db, 'K4', {})).toBe('200');
400+
});
360401
});
361402
});
362403
});

0 commit comments

Comments
 (0)