@nxtedition/rocksdb 8.0.4 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/BUILDING.md +2 -2
  2. package/binding.cc +72 -2
  3. package/deps/rocksdb/rocksdb/CMakeLists.txt +7 -0
  4. package/deps/rocksdb/rocksdb/Makefile +13 -1
  5. package/deps/rocksdb/rocksdb/db/builder.cc +13 -4
  6. package/deps/rocksdb/rocksdb/db/builder.h +2 -1
  7. package/deps/rocksdb/rocksdb/db/c.cc +6 -0
  8. package/deps/rocksdb/rocksdb/db/column_family.cc +1 -0
  9. package/deps/rocksdb/rocksdb/db/compaction/compaction.cc +18 -4
  10. package/deps/rocksdb/rocksdb/db/compaction/compaction_job.cc +2 -0
  11. package/deps/rocksdb/rocksdb/db/compaction/compaction_job_test.cc +2 -1
  12. package/deps/rocksdb/rocksdb/db/compaction/compaction_outputs.cc +22 -2
  13. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker.cc +5 -1
  14. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_level.cc +14 -14
  15. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_test.cc +1 -2
  16. package/deps/rocksdb/rocksdb/db/db_bloom_filter_test.cc +2 -3
  17. package/deps/rocksdb/rocksdb/db/db_compaction_test.cc +225 -0
  18. package/deps/rocksdb/rocksdb/db/db_impl/db_impl.cc +8 -9
  19. package/deps/rocksdb/rocksdb/db/db_impl/db_impl.h +0 -8
  20. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_compaction_flush.cc +63 -23
  21. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_experimental.cc +2 -1
  22. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_open.cc +12 -8
  23. package/deps/rocksdb/rocksdb/db/db_range_del_test.cc +115 -2
  24. package/deps/rocksdb/rocksdb/db/experimental.cc +2 -1
  25. package/deps/rocksdb/rocksdb/db/external_sst_file_basic_test.cc +1 -0
  26. package/deps/rocksdb/rocksdb/db/external_sst_file_ingestion_job.cc +88 -12
  27. package/deps/rocksdb/rocksdb/db/external_sst_file_ingestion_job.h +38 -1
  28. package/deps/rocksdb/rocksdb/db/external_sst_file_test.cc +14 -110
  29. package/deps/rocksdb/rocksdb/db/flush_job.cc +2 -3
  30. package/deps/rocksdb/rocksdb/db/import_column_family_job.cc +1 -1
  31. package/deps/rocksdb/rocksdb/db/repair.cc +2 -1
  32. package/deps/rocksdb/rocksdb/db/version_builder_test.cc +41 -39
  33. package/deps/rocksdb/rocksdb/db/version_edit.cc +12 -0
  34. package/deps/rocksdb/rocksdb/db/version_edit.h +18 -6
  35. package/deps/rocksdb/rocksdb/db/version_edit_test.cc +9 -9
  36. package/deps/rocksdb/rocksdb/db/version_set.cc +12 -6
  37. package/deps/rocksdb/rocksdb/db/version_set_test.cc +23 -9
  38. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_common.h +1 -0
  39. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_gflags.cc +4 -0
  40. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_test_base.cc +5 -0
  41. package/deps/rocksdb/rocksdb/include/rocksdb/c.h +4 -0
  42. package/deps/rocksdb/rocksdb/include/rocksdb/listener.h +7 -1
  43. package/deps/rocksdb/rocksdb/include/rocksdb/options.h +2 -1
  44. package/deps/rocksdb/rocksdb/include/rocksdb/utilities/backup_engine.h +69 -9
  45. package/deps/rocksdb/rocksdb/utilities/backup/backup_engine.cc +245 -74
  46. package/deps/rocksdb/rocksdb/utilities/backup/backup_engine_test.cc +195 -4
  47. package/index.js +17 -0
  48. package/max_rev_operator.h +100 -0
  49. package/package.json +1 -1
  50. package/prebuilds/darwin-arm64/node.napi.node +0 -0
  51. package/prebuilds/darwin-x64/node.napi.node +0 -0
  52. package/prebuilds/linux-x64/node.napi.node +0 -0
@@ -16,9 +16,11 @@
16
16
  #include <atomic>
17
17
  #include <cstddef>
18
18
  #include <cstdint>
19
+ #include <exception>
19
20
  #include <limits>
20
21
  #include <memory>
21
22
  #include <random>
23
+ #include <stdexcept>
22
24
  #include <string>
23
25
  #include <utility>
24
26
 
@@ -754,7 +756,7 @@ class BackupEngineTest : public testing::Test {
754
756
  void CloseBackupEngine() { backup_engine_.reset(nullptr); }
755
757
 
756
758
  // cross-cutting test of GetBackupInfo
757
- void AssertBackupInfoConsistency() {
759
+ void AssertBackupInfoConsistency(bool allow_excluded = false) {
758
760
  std::vector<BackupInfo> backup_info;
759
761
  backup_engine_->GetBackupInfo(&backup_info, /*with file details*/ true);
760
762
  std::map<std::string, uint64_t> file_sizes;
@@ -774,6 +776,9 @@ class BackupEngineTest : public testing::Test {
774
776
  sum_for_backup += file.size;
775
777
  }
776
778
  ASSERT_EQ(backup.size, sum_for_backup);
779
+ if (!allow_excluded) {
780
+ ASSERT_EQ(backup.excluded_files.size(), 0);
781
+ }
777
782
  }
778
783
 
779
784
  std::vector<BackupID> corrupt_backup_ids;
@@ -3094,10 +3099,25 @@ TEST_F(BackupEngineTest, OpenBackupAsReadOnlyDB) {
3094
3099
 
3095
3100
  TEST_F(BackupEngineTest, ProgressCallbackDuringBackup) {
3096
3101
  DestroyDBWithoutCheck(dbname_, options_);
3097
- // Too big for this small DB
3098
- engine_options_->callback_trigger_interval_size = 100000;
3102
+
3099
3103
  OpenDBAndBackupEngine(true);
3100
3104
  FillDB(db_.get(), 0, 100);
3105
+
3106
+ // First test exception handling
3107
+ // Easily small enough for this small DB
3108
+ engine_options_->callback_trigger_interval_size = 1000;
3109
+ OpenBackupEngine();
3110
+ ASSERT_TRUE(
3111
+ backup_engine_->CreateNewBackup(db_.get(), true, []() { throw 42; })
3112
+ .IsAborted());
3113
+ ASSERT_TRUE(backup_engine_
3114
+ ->CreateNewBackup(db_.get(), true,
3115
+ []() { throw std::out_of_range("blah"); })
3116
+ .IsAborted());
3117
+
3118
+ // Too big for this small DB
3119
+ engine_options_->callback_trigger_interval_size = 100000;
3120
+ OpenBackupEngine();
3101
3121
  bool is_callback_invoked = false;
3102
3122
  ASSERT_OK(backup_engine_->CreateNewBackup(
3103
3123
  db_.get(), true,
@@ -4180,7 +4200,7 @@ TEST_F(BackupEngineTest, FileTemperatures) {
4180
4200
  &info, /*include_file_details*/ true));
4181
4201
  ASSERT_GT(info.file_details.size(), 2);
4182
4202
  for (auto& e : info.file_details) {
4183
- ASSERT_EQ(expected_temps[e.file_number], e.temperature);
4203
+ EXPECT_EQ(expected_temps[e.file_number], e.temperature);
4184
4204
  }
4185
4205
 
4186
4206
  // Restore backup to another virtual (tiered) dir
@@ -4202,6 +4222,177 @@ TEST_F(BackupEngineTest, FileTemperatures) {
4202
4222
  }
4203
4223
  }
4204
4224
 
4225
+ TEST_F(BackupEngineTest, ExcludeFiles) {
4226
+ // Required for excluding files
4227
+ engine_options_->schema_version = 2;
4228
+
4229
+ // Need a sufficent set of file numbers
4230
+ options_.level0_file_num_compaction_trigger = 100;
4231
+
4232
+ OpenDBAndBackupEngine(true, false, kShareWithChecksum);
4233
+ // Need a sufficent set of file numbers
4234
+ const int keys_iteration = 5000;
4235
+ FillDB(db_.get(), 0, keys_iteration / 3);
4236
+ FillDB(db_.get(), keys_iteration / 3, keys_iteration * 2 / 3);
4237
+ FillDB(db_.get(), keys_iteration * 2 / 3, keys_iteration);
4238
+ CloseAndReopenDB();
4239
+
4240
+ BackupEngine* alt_backup_engine;
4241
+ BackupEngineOptions alt_engine_options{*engine_options_};
4242
+ // Use an alternate Env to test that support
4243
+ std::string backup_alt_chroot = test::PerThreadDBPath("db_alt_backups");
4244
+ EXPECT_OK(Env::Default()->CreateDirIfMissing(backup_alt_chroot));
4245
+ alt_engine_options.backup_dir = "/altbk";
4246
+ std::shared_ptr<FileSystem> alt_fs{
4247
+ NewChrootFileSystem(FileSystem::Default(), backup_alt_chroot)};
4248
+ std::unique_ptr<Env> alt_env{new CompositeEnvWrapper(Env::Default(), alt_fs)};
4249
+ alt_engine_options.backup_env = alt_env.get();
4250
+
4251
+ ASSERT_OK(BackupEngine::Open(test_db_env_.get(), alt_engine_options,
4252
+ &alt_backup_engine));
4253
+
4254
+ // Ensure each backup is same set of files
4255
+ db_.reset();
4256
+ DB* db = nullptr;
4257
+ ASSERT_OK(DB::OpenForReadOnly(options_, dbname_, &db));
4258
+
4259
+ // A callback that throws should cleanly fail the backup creation.
4260
+ // Do this early to ensure later operations still work.
4261
+ CreateBackupOptions cbo;
4262
+ cbo.exclude_files_callback = [](MaybeExcludeBackupFile* /*files_begin*/,
4263
+ MaybeExcludeBackupFile* /*files_end*/) {
4264
+ throw 42;
4265
+ };
4266
+ ASSERT_TRUE(backup_engine_->CreateNewBackup(cbo, db).IsAborted());
4267
+ cbo.exclude_files_callback = [](MaybeExcludeBackupFile* /*files_begin*/,
4268
+ MaybeExcludeBackupFile* /*files_end*/) {
4269
+ throw std::out_of_range("blah");
4270
+ };
4271
+ ASSERT_TRUE(backup_engine_->CreateNewBackup(cbo, db).IsAborted());
4272
+
4273
+ // Include files only in given bucket, based on modulus and remainder
4274
+ constexpr int modulus = 4;
4275
+ int remainder = 0;
4276
+
4277
+ cbo.exclude_files_callback = [&remainder](MaybeExcludeBackupFile* files_begin,
4278
+ MaybeExcludeBackupFile* files_end) {
4279
+ for (auto* f = files_begin; f != files_end; ++f) {
4280
+ std::string s = StringSplit(f->info.relative_file, '/').back();
4281
+ s = s.substr(0, s.find("_"));
4282
+ int64_t num = std::strtoll(s.c_str(), nullptr, /*base*/ 10);
4283
+ // Exclude if not a match
4284
+ f->exclude_decision = (num % modulus) != remainder;
4285
+ }
4286
+ };
4287
+
4288
+ BackupID first_id{};
4289
+ BackupID last_alt_id{};
4290
+ remainder = 0;
4291
+ ASSERT_OK(backup_engine_->CreateNewBackup(cbo, db, &first_id));
4292
+ AssertBackupInfoConsistency(/*allow excluded*/ true);
4293
+ remainder = 1;
4294
+ ASSERT_OK(alt_backup_engine->CreateNewBackup(cbo, db));
4295
+ AssertBackupInfoConsistency(/*allow excluded*/ true);
4296
+ remainder = 2;
4297
+ ASSERT_OK(backup_engine_->CreateNewBackup(cbo, db));
4298
+ AssertBackupInfoConsistency(/*allow excluded*/ true);
4299
+ remainder = 3;
4300
+ ASSERT_OK(alt_backup_engine->CreateNewBackup(cbo, db, &last_alt_id));
4301
+ AssertBackupInfoConsistency(/*allow excluded*/ true);
4302
+
4303
+ // Close DB
4304
+ ASSERT_OK(db->Close());
4305
+ delete db;
4306
+ db = nullptr;
4307
+
4308
+ for (auto be_pair :
4309
+ {std::make_pair(backup_engine_.get(), alt_backup_engine),
4310
+ std::make_pair(alt_backup_engine, backup_engine_.get())}) {
4311
+ DestroyDB(dbname_, options_);
4312
+ RestoreOptions ro;
4313
+ // Fails without alternate dir
4314
+ ASSERT_TRUE(be_pair.first->RestoreDBFromLatestBackup(dbname_, dbname_, ro)
4315
+ .IsInvalidArgument());
4316
+
4317
+ DestroyDB(dbname_, options_);
4318
+ // Works with alternate dir
4319
+ ro.alternate_dirs.push_front(be_pair.second);
4320
+ ASSERT_OK(be_pair.first->RestoreDBFromLatestBackup(dbname_, dbname_, ro));
4321
+
4322
+ // Check DB contents
4323
+ db = OpenDB();
4324
+ AssertExists(db, 0, keys_iteration);
4325
+ delete db;
4326
+ }
4327
+
4328
+ // Should still work after close and re-open
4329
+ CloseBackupEngine();
4330
+ OpenBackupEngine();
4331
+
4332
+ for (auto be_pair :
4333
+ {std::make_pair(backup_engine_.get(), alt_backup_engine),
4334
+ std::make_pair(alt_backup_engine, backup_engine_.get())}) {
4335
+ DestroyDB(dbname_, options_);
4336
+ RestoreOptions ro;
4337
+ ro.alternate_dirs.push_front(be_pair.second);
4338
+ ASSERT_OK(be_pair.first->RestoreDBFromLatestBackup(dbname_, dbname_, ro));
4339
+ }
4340
+
4341
+ // Deletion semantics are tricky when within a single backup dir one backup
4342
+ // includes a file and the other backup excluded the file. The excluded one
4343
+ // does not have a persistent record of metadata like file checksum, etc.
4344
+ // Although it would be possible to amend the backup with the excluded file,
4345
+ // that is not currently supported (unless you open the backup as read-only
4346
+ // DB and take another backup of it). The "excluded" reference to the file
4347
+ // is like a weak reference: it doesn't prevent the file from being deleted
4348
+ // if all the backups with "included" references to it are deleted.
4349
+ CloseBackupEngine();
4350
+ OpenBackupEngine();
4351
+
4352
+ AssertBackupInfoConsistency(/*allow excluded*/ true);
4353
+
4354
+ ASSERT_OK(backup_engine_->DeleteBackup(first_id));
4355
+ ASSERT_OK(alt_backup_engine->DeleteBackup(last_alt_id));
4356
+
4357
+ // Includes check for any leaked backup files
4358
+ AssertBackupInfoConsistency(/*allow excluded*/ true);
4359
+
4360
+ // Excluded file(s) deleted, unable to restore
4361
+ for (auto be_pair :
4362
+ {std::make_pair(backup_engine_.get(), alt_backup_engine),
4363
+ std::make_pair(alt_backup_engine, backup_engine_.get())}) {
4364
+ RestoreOptions ro;
4365
+ ro.alternate_dirs.push_front(be_pair.second);
4366
+ ASSERT_TRUE(be_pair.first->RestoreDBFromLatestBackup(dbname_, dbname_, ro)
4367
+ .IsInvalidArgument());
4368
+ }
4369
+
4370
+ // Close & Re-open (no crash, etc.)
4371
+ CloseBackupEngine();
4372
+ OpenBackupEngine();
4373
+
4374
+ AssertBackupInfoConsistency(/*allow excluded*/ true);
4375
+
4376
+ // Excluded file(s) deleted, unable to restore
4377
+ for (auto be_pair :
4378
+ {std::make_pair(backup_engine_.get(), alt_backup_engine),
4379
+ std::make_pair(alt_backup_engine, backup_engine_.get())}) {
4380
+ RestoreOptions ro;
4381
+ ro.alternate_dirs.push_front(be_pair.second);
4382
+ ASSERT_TRUE(be_pair.first->RestoreDBFromLatestBackup(dbname_, dbname_, ro)
4383
+ .IsInvalidArgument());
4384
+ }
4385
+
4386
+ // Ensure files are not leaked after removing everything.
4387
+ ASSERT_OK(backup_engine_->DeleteBackup(first_id + 1));
4388
+ ASSERT_OK(alt_backup_engine->DeleteBackup(last_alt_id - 1));
4389
+
4390
+ // Includes check for leaked backups files
4391
+ AssertBackupInfoConsistency(/*allow excluded*/ false);
4392
+
4393
+ delete alt_backup_engine;
4394
+ }
4395
+
4205
4396
  } // namespace
4206
4397
 
4207
4398
  } // namespace ROCKSDB_NAMESPACE
package/index.js CHANGED
@@ -168,6 +168,23 @@ class RocksLevel extends AbstractLevel {
168
168
  return callback[kPromise]
169
169
  }
170
170
 
171
+ _getMergeOperands(key, options, callback) {
172
+ callback = fromCallback(callback, kPromise)
173
+
174
+ try {
175
+ this[kRef]()
176
+ binding.db_get_merge_operands(this[kContext], key, options ?? EMPTY, (err, val) => {
177
+ callback(err, val)
178
+ this[kUnref]()
179
+ })
180
+ } catch (err) {
181
+ process.nextTick(callback, err)
182
+ this[kUnref]()
183
+ }
184
+
185
+ return callback[kPromise]
186
+ }
187
+
171
188
  _del (key, options, callback) {
172
189
  callback = fromCallback(callback, kPromise)
173
190
 
@@ -0,0 +1,100 @@
1
+ #pragma once
2
+
3
+ #include <rocksdb/slice.h>
4
+ #include <rocksdb/merge_operator.h>
5
+
6
+ int compareRev(const rocksdb::Slice& a, const rocksdb::Slice& b) {
7
+ auto indexA = 0UL;
8
+ auto indexB = 0UL;
9
+ const auto endA = a.size();
10
+ const auto endB = b.size();
11
+
12
+ // Compare the revision number
13
+ auto result = 0;
14
+ const auto end = std::min(endA, endB);
15
+ while (indexA < end && indexB < end) {
16
+ const auto ac = a[indexA++];
17
+ const auto bc = b[indexB++];
18
+
19
+ if (ac == '-') {
20
+ if (bc == '-') {
21
+ break;
22
+ }
23
+ return -1;
24
+ } else if (bc == '-') {
25
+ return 1;
26
+ }
27
+
28
+ if (!result) {
29
+ result = ac == bc ? 0 : ac < bc ? -1 : 1;
30
+ }
31
+ }
32
+
33
+ if (result) {
34
+ return result;
35
+ }
36
+
37
+ // Compare the rest
38
+ while (indexA < end && indexB < end) {
39
+ const auto ac = a[indexA++];
40
+ const auto bc = b[indexB++];
41
+ if (ac != bc) {
42
+ return ac < bc ? -1 : 1;
43
+ }
44
+ }
45
+
46
+ return endA - endB;
47
+ }
48
+
49
+ class MaxRevOperator : public rocksdb::MergeOperator {
50
+ public:
51
+ bool FullMergeV2(const MergeOperationInput& merge_in,
52
+ MergeOperationOutput* merge_out) const override {
53
+ rocksdb::Slice& max = merge_out->existing_operand;
54
+ if (merge_in.existing_value) {
55
+ max = rocksdb::Slice(merge_in.existing_value->data(),
56
+ merge_in.existing_value->size());
57
+ } else if (max.data() == nullptr) {
58
+ max = rocksdb::Slice();
59
+ }
60
+
61
+ for (const auto& op : merge_in.operand_list) {
62
+ if (compareRev(max, op) < 0) {
63
+ max = op;
64
+ }
65
+ }
66
+
67
+ return true;
68
+ }
69
+
70
+ bool PartialMerge(const rocksdb::Slice& /*key*/, const rocksdb::Slice& left_operand,
71
+ const rocksdb::Slice& right_operand, std::string* new_value,
72
+ rocksdb::Logger* /*logger*/) const override {
73
+ if (compareRev(left_operand, right_operand) >= 0) {
74
+ new_value->assign(left_operand.data(), left_operand.size());
75
+ } else {
76
+ new_value->assign(right_operand.data(), right_operand.size());
77
+ }
78
+ return true;
79
+ }
80
+
81
+ bool PartialMergeMulti(const rocksdb::Slice& /*key*/,
82
+ const std::deque<rocksdb::Slice>& operand_list,
83
+ std::string* new_value,
84
+ rocksdb::Logger* /*logger*/) const override {
85
+ rocksdb::Slice max;
86
+ for (const auto& operand : operand_list) {
87
+ if (compareRev(max, operand) < 0) {
88
+ max = operand;
89
+ }
90
+ }
91
+
92
+ new_value->assign(max.data(), max.size());
93
+ return true;
94
+ }
95
+
96
+ static const char* kClassName() { return "MaxRevOperator"; }
97
+ static const char* kNickName() { return "maxRev"; }
98
+ const char* Name() const override { return kClassName(); }
99
+ const char* NickName() const override { return kNickName(); }
100
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/rocksdb",
3
- "version": "8.0.4",
3
+ "version": "8.1.0",
4
4
  "description": "A low-level Node.js RocksDB binding",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
Binary file
Binary file