20 #include <ripple/basics/contract.h>
21 #include <ripple/nodestore/impl/codec.h>
22 #include <ripple/beast/clock/basic_seconds_clock.h>
23 #include <ripple/beast/rfc2616.h>
24 #include <ripple/beast/core/LexicalCast.h>
25 #include <ripple/beast/unit_test.h>
26 #include <nudb/create.hpp>
27 #include <nudb/detail/format.hpp>
28 #include <nudb/xxhasher.hpp>
29 #include <boost/beast/core/string.hpp>
30 #include <boost/regex.hpp>
37 #include <ripple/unity/rocksdb.h>
89 template <
class Rep,
class Period>
107 os << date::round<nanoseconds>(d).count();
123 os << date::round<microseconds>(d).count();
139 os << date::round<milliseconds>(d).count();
155 os << date::round<seconds>(d).count();
171 os << date::round<minutes>(d).count();
178 template <
class Period,
class Rep>
204 bool estimate_ =
false;
218 auto const now = clock_type::now();
222 auto const elapsed = now - start_;
229 else if (now - report_ <
235 elapsed.count() / double(work);
238 (work_ - work) * rate));
241 " (" << work <<
" of " << work_ <<
243 ", " << (work - prev_) <<
256 clock_type::now() - start_);
264 static boost::regex
const re1 (
267 "([a-zA-Z][_a-zA-Z0-9]*)"
273 , boost::regex_constants::optimize
279 for (
auto const& kv : v)
282 if (! boost::regex_match (kv, m, re1))
283 Throw<std::runtime_error> (
284 "invalid parameter " + kv);
286 map.emplace(m[1], m[2]);
288 Throw<std::runtime_error> (
289 "duplicate parameter " + m[1]);
296 #if RIPPLE_ROCKSDB_AVAILABLE
298 class import_test :
public beast::unit_test::suite
304 testcase(beast::unit_test::abort_on_fail) << arg();
306 using namespace nudb;
307 using namespace nudb::detail;
311 bool usage = args.empty();
314 args.find(
"from") == args.end())
317 "Missing parameter: from";
321 args.find(
"to") == args.end())
324 "Missing parameter: to";
328 args.find(
"buffer") == args.end())
331 "Missing parameter: buffer";
339 "--unittest-arg=from=<from>,to=<to>,buffer=<buffer>\n" <<
340 "from: RocksDB database to import from\n" <<
341 "to: NuDB database to import to\n" <<
342 "buffer: Buffer size (bigger is faster)\n" <<
343 "NuDB database must not already exist.";
353 auto const from_path = args.at(
"from");
354 auto const to_path = args.at(
"to");
356 using hash_type = nudb::xxhasher;
357 auto const bulk_size = 64 * 1024 * 1024;
358 float const load_factor = 0.5;
360 auto const dp = to_path +
".dat";
361 auto const kp = to_path +
".key";
367 "from: " << from_path <<
"\n"
368 "to: " << to_path <<
"\n"
369 "buffer: " << buffer_size;
373 rocksdb::Options options;
374 options.create_if_missing =
false;
375 options.max_open_files = 2000;
376 rocksdb::DB* pdb =
nullptr;
378 rocksdb::DB::OpenForReadOnly(
379 options, from_path, &pdb);
380 if (!
status.ok () || ! pdb)
381 Throw<std::runtime_error> (
382 "Can't open '" + from_path +
"': " +
390 dh.version = currentVersion;
397 df.create(file_mode::append, dp, ec);
399 Throw<nudb::system_error>(ec);
400 bulk_writer<native_file> dw(
404 auto os = dw.prepare(dat_file_header::size, ec);
406 Throw<nudb::system_error>(ec);
409 rocksdb::ReadOptions options;
410 options.verify_checksums =
false;
411 options.fill_cache =
false;
413 db->NewIterator(options));
416 for (it->SeekToFirst (); it->Valid (); it->Next())
418 if (it->key().size() != 32)
419 Throw<std::runtime_error> (
420 "Unexpected key size " +
422 void const*
const key = it->key().data();
423 void const*
const data = it->value().data();
424 auto const size = it->value().size();
430 clean.get(), size, buf);
435 out.first,
out.second, buf2);
436 BEAST_EXPECT(
check.second == size);
438 check.first, clean.get(), size) == 0);
441 auto os = dw.prepare(
442 field<uint48_t>::size +
446 Throw<nudb::system_error>(ec);
447 write<uint48_t>(os,
out.second);
456 Throw<nudb::system_error>(ec);
462 auto const df_size = df.size(ec);
464 Throw<nudb::system_error>(ec);
467 kh.version = currentVersion;
469 kh.appnum = dh.appnum;
471 kh.salt = make_salt();
472 kh.pepper = pepper<hash_type>(kh.salt);
473 kh.block_size = block_size(kp);
474 kh.load_factor = std::min<std::size_t>(
475 65536.0 * load_factor, 65535);
476 kh.buckets =
std::ceil(nitems / (bucket_capacity(
477 kh.block_size) * load_factor));
478 kh.modulus = ceil_pow2(kh.buckets);
480 kf.create(file_mode::append, kp, ec);
482 Throw<nudb::system_error>(ec);
483 buffer buf(kh.block_size);
486 ostream os(buf.get(), kh.block_size);
488 kf.write(0, buf.get(), kh.block_size, ec);
490 Throw<nudb::system_error>(ec);
495 auto const buckets = std::max<std::size_t>(1,
496 buffer_size / kh.block_size);
497 buf.reserve(buckets * kh.block_size);
499 (kh.buckets + buckets - 1) / buckets;
501 "items: " << nitems <<
"\n"
502 "buckets: " << kh.buckets <<
"\n"
503 "data: " << df_size <<
"\n"
504 "passes: " << passes;
505 progress p(df_size * passes);
511 b0 + buckets, kh.buckets);
513 auto const bn = b1 - b0;
517 bucket b(kh.block_size,
518 buf.get() + i * kh.block_size,
523 bulk_reader<native_file> r(
524 df, dat_file_header::size,
528 auto const offset = r.offset();
532 field<uint48_t>::size, ec);
534 Throw<nudb::system_error>(ec);
535 read<uint48_t>(is, size);
543 Throw<nudb::system_error>(ec);
545 is.data(dh.key_size);
546 auto const h = hash<hash_type>(
547 key, kh.key_size, kh.salt);
548 auto const n = bucket_index(
549 h, kh.buckets, kh.modulus);
551 npass * df_size + r.offset());
552 if (n < b0 || n >= b1)
554 bucket b(kh.block_size, buf.get() +
555 (n - b0) * kh.block_size);
556 maybe_spill(b, dw, ec);
558 Throw<nudb::system_error>(ec);
559 b.insert(offset, size, h);
566 field<std::uint16_t>::size, ec);
568 Throw<nudb::system_error>(ec);
569 read<std::uint16_t>(is, size);
572 Throw<nudb::system_error>(ec);
575 kf.write((b0 + 1) * kh.block_size,
576 buf.get(), bn * kh.block_size, ec);
578 Throw<nudb::system_error>(ec);
583 Throw<nudb::system_error>(ec);