2016-02-09 23:12:00 +00:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-15 23:03:42 +00:00
|
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
|
|
// (found in the LICENSE.Apache file in the root directory).
|
2014-10-28 18:54:33 +00:00
|
|
|
//
|
|
|
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2017-10-06 01:00:38 +00:00
|
|
|
#include "db/column_family.h"
|
2021-09-10 16:59:05 +00:00
|
|
|
#include "db/log_writer.h"
|
|
|
|
#include "db/version_set.h"
|
2023-11-11 16:11:11 +00:00
|
|
|
#include "util/autovector.h"
|
2014-10-28 18:54:33 +00:00
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
namespace ROCKSDB_NAMESPACE {
|
2014-10-28 18:54:33 +00:00
|
|
|
|
|
|
|
class MemTable;
|
2017-10-06 01:00:38 +00:00
|
|
|
struct SuperVersion;
|
|
|
|
|
|
|
|
struct SuperVersionContext {
|
|
|
|
struct WriteStallNotification {
|
|
|
|
WriteStallInfo write_stall_info;
|
2021-06-16 23:50:43 +00:00
|
|
|
const ImmutableOptions* immutable_options;
|
2017-10-06 01:00:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
autovector<SuperVersion*> superversions_to_free;
|
2018-05-09 17:04:55 +00:00
|
|
|
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
2017-10-06 01:00:38 +00:00
|
|
|
autovector<WriteStallNotification> write_stall_notifications;
|
2018-05-09 17:04:55 +00:00
|
|
|
#endif
|
2018-11-09 19:17:34 +00:00
|
|
|
std::unique_ptr<SuperVersion>
|
|
|
|
new_superversion; // if nullptr no new superversion
|
2017-10-06 01:00:38 +00:00
|
|
|
|
Support returning write unix time in iterator property (#12428)
Summary:
This PR adds support to return data's approximate unix write time in the iterator property API. The general implementation is:
1) If the entry comes from a SST file, the sequence number to time mapping recorded in that file's table properties will be used to deduce the entry's write time from its sequence number. If no such recording is available, `std::numeric_limits<uint64_t>::max()` is returned to indicate the write time is unknown except if the entry's sequence number is zero, in which case, 0 is returned. This also means that even if `preclude_last_level_data_seconds` and `preserve_internal_time_seconds` can be toggled off between DB reopens, as long as the SST file's table property has the mapping available, the entry's write time can be deduced and returned.
2) If the entry comes from memtable, we will use the DB's sequence number to write time mapping to do similar things. A copy of the DB's seqno to write time mapping is kept in SuperVersion to allow iterators to have lock free access. This also means a new `SuperVersion` is installed each time DB's seqno to time mapping updates, which is originally proposed by Peter in https://github.com/facebook/rocksdb/issues/11928 . Similarly, if the feature is not enabled, `std::numeric_limits<uint64_t>::max()` is returned to indicate the write time is unknown.
Needed follow up:
1) The write time for `kTypeValuePreferredSeqno` should be special cased, where it's already specified by the user, so we can directly return it.
2) Flush job can be updated to use DB's seqno to time mapping copy in the SuperVersion.
3) Handle the case when `TimedPut` is called with a write time that is `std::numeric_limits<uint64_t>::max()`. We can make it a regular `Put`.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12428
Test Plan: Added unit test
Reviewed By: pdillinger
Differential Revision: D54967067
Pulled By: jowlyzhang
fbshipit-source-id: c795b1b7ec142e09e53f2ed3461cf719833cb37a
2024-03-15 22:37:37 +00:00
|
|
|
// If not nullptr, a new seqno to time mapping is available to be installed.
|
|
|
|
// Otherwise, make a shared copy of the one in the existing SuperVersion and
|
|
|
|
// carry it over to the new SuperVersion. This is moved to the SuperVersion
|
|
|
|
// during installation.
|
|
|
|
std::shared_ptr<const SeqnoToTimeMapping> new_seqno_to_time_mapping{nullptr};
|
|
|
|
|
2017-10-06 01:00:38 +00:00
|
|
|
explicit SuperVersionContext(bool create_superversion = false)
|
2022-11-02 21:34:24 +00:00
|
|
|
: new_superversion(create_superversion ? new SuperVersion() : nullptr) {}
|
2017-10-06 01:00:38 +00:00
|
|
|
|
2022-03-18 23:35:51 +00:00
|
|
|
explicit SuperVersionContext(SuperVersionContext&& other) noexcept
|
2018-08-04 00:34:07 +00:00
|
|
|
: superversions_to_free(std::move(other.superversions_to_free)),
|
|
|
|
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
|
|
|
write_stall_notifications(std::move(other.write_stall_notifications)),
|
|
|
|
#endif
|
|
|
|
new_superversion(std::move(other.new_superversion)) {
|
|
|
|
}
|
2022-03-18 23:35:51 +00:00
|
|
|
// No copies
|
|
|
|
SuperVersionContext(const SuperVersionContext& other) = delete;
|
|
|
|
void operator=(const SuperVersionContext& other) = delete;
|
2018-08-04 00:34:07 +00:00
|
|
|
|
2017-10-06 01:00:38 +00:00
|
|
|
void NewSuperVersion() {
|
2018-11-09 19:17:34 +00:00
|
|
|
new_superversion = std::unique_ptr<SuperVersion>(new SuperVersion());
|
2017-10-06 01:00:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool HaveSomethingToDelete() const {
|
2018-05-09 17:04:55 +00:00
|
|
|
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
2022-11-02 21:34:24 +00:00
|
|
|
return !superversions_to_free.empty() || !write_stall_notifications.empty();
|
2018-05-09 17:04:55 +00:00
|
|
|
#else
|
|
|
|
return !superversions_to_free.empty();
|
|
|
|
#endif
|
2017-10-06 01:00:38 +00:00
|
|
|
}
|
|
|
|
|
2021-05-05 20:59:21 +00:00
|
|
|
void PushWriteStallNotification(WriteStallCondition old_cond,
|
|
|
|
WriteStallCondition new_cond,
|
|
|
|
const std::string& name,
|
|
|
|
const ImmutableOptions* ioptions) {
|
2023-01-27 21:14:19 +00:00
|
|
|
#if !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
2017-10-06 01:00:38 +00:00
|
|
|
WriteStallNotification notif;
|
|
|
|
notif.write_stall_info.cf_name = name;
|
|
|
|
notif.write_stall_info.condition.prev = old_cond;
|
|
|
|
notif.write_stall_info.condition.cur = new_cond;
|
2021-06-16 23:50:43 +00:00
|
|
|
notif.immutable_options = ioptions;
|
2017-10-06 01:00:38 +00:00
|
|
|
write_stall_notifications.push_back(notif);
|
2018-04-13 00:55:14 +00:00
|
|
|
#else
|
|
|
|
(void)old_cond;
|
|
|
|
(void)new_cond;
|
|
|
|
(void)name;
|
|
|
|
(void)ioptions;
|
2023-01-27 21:14:19 +00:00
|
|
|
#endif // !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
2017-10-06 01:00:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Clean() {
|
2023-01-27 21:14:19 +00:00
|
|
|
#if !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
2017-10-06 01:00:38 +00:00
|
|
|
// notify listeners on changed write stall conditions
|
|
|
|
for (auto& notif : write_stall_notifications) {
|
2021-06-16 23:50:43 +00:00
|
|
|
for (auto& listener : notif.immutable_options->listeners) {
|
2017-10-06 01:00:38 +00:00
|
|
|
listener->OnStallConditionsChanged(notif.write_stall_info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write_stall_notifications.clear();
|
2023-01-27 21:14:19 +00:00
|
|
|
#endif
|
2017-10-06 01:00:38 +00:00
|
|
|
// free superversions
|
|
|
|
for (auto s : superversions_to_free) {
|
|
|
|
delete s;
|
|
|
|
}
|
|
|
|
superversions_to_free.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
~SuperVersionContext() {
|
2018-05-09 17:04:55 +00:00
|
|
|
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
|
|
|
assert(write_stall_notifications.empty());
|
|
|
|
#endif
|
|
|
|
assert(superversions_to_free.empty());
|
2017-10-06 01:00:38 +00:00
|
|
|
}
|
|
|
|
};
|
2014-10-28 18:54:33 +00:00
|
|
|
|
|
|
|
struct JobContext {
|
|
|
|
inline bool HaveSomethingToDelete() const {
|
2020-04-30 18:23:32 +00:00
|
|
|
return !(full_scan_candidate_files.empty() && sst_delete_files.empty() &&
|
|
|
|
blob_delete_files.empty() && log_delete_files.empty() &&
|
|
|
|
manifest_delete_files.empty());
|
2018-01-12 21:16:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool HaveSomethingToClean() const {
|
2018-08-04 00:34:07 +00:00
|
|
|
bool sv_have_sth = false;
|
|
|
|
for (const auto& sv_ctx : superversion_contexts) {
|
|
|
|
if (sv_ctx.HaveSomethingToDelete()) {
|
|
|
|
sv_have_sth = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-01-12 21:16:39 +00:00
|
|
|
return memtables_to_free.size() > 0 || logs_to_free.size() > 0 ||
|
2021-08-24 06:08:25 +00:00
|
|
|
job_snapshot != nullptr || sv_have_sth;
|
2014-10-28 18:54:33 +00:00
|
|
|
}
|
|
|
|
|
CompactionIterator sees consistent view of which keys are committed (#9830)
Summary:
**This PR does not affect the functionality of `DB` and write-committed transactions.**
`CompactionIterator` uses `KeyCommitted(seq)` to determine if a key in the database is committed.
As the name 'write-committed' implies, if write-committed policy is used, a key exists in the database only if
it is committed. In fact, the implementation of `KeyCommitted()` is as follows:
```
inline bool KeyCommitted(SequenceNumber seq) {
// For non-txn-db and write-committed, snapshot_checker_ is always nullptr.
return snapshot_checker_ == nullptr ||
snapshot_checker_->CheckInSnapshot(seq, kMaxSequence) == SnapshotCheckerResult::kInSnapshot;
}
```
With that being said, we focus on write-prepared/write-unprepared transactions.
A few notes:
- A key can exist in the db even if it's uncommitted. Therefore, we rely on `snapshot_checker_` to determine data visibility. We also require that all writes go through transaction API instead of the raw `WriteBatch` + `Write`, thus at most one uncommitted version of one user key can exist in the database.
- `CompactionIterator` outputs a key as long as the key is uncommitted.
Due to the above reasons, it is possible that `CompactionIterator` decides to output an uncommitted key without
doing further checks on the key (`NextFromInput()`). By the time the key is being prepared for output, the key becomes
committed because the `snapshot_checker_(seq, kMaxSequence)` becomes true in the implementation of `KeyCommitted()`.
Then `CompactionIterator` will try to zero its sequence number and hit assertion error if the key is a tombstone.
To fix this issue, we should make the `CompactionIterator` see a consistent view of the input keys. Note that
for write-prepared/write-unprepared, the background flush/compaction jobs already take a "job snapshot" before starting
processing keys. The job snapshot is released only after the entire flush/compaction finishes. We can use this snapshot
to determine whether a key is committed or not with minor change to `KeyCommitted()`.
```
inline bool KeyCommitted(SequenceNumber sequence) {
// For non-txn-db and write-committed, snapshot_checker_ is always nullptr.
return snapshot_checker_ == nullptr ||
snapshot_checker_->CheckInSnapshot(sequence, job_snapshot_) ==
SnapshotCheckerResult::kInSnapshot;
}
```
As a result, whether a key is committed or not will remain a constant throughout compaction, causing no trouble
for `CompactionIterator`s assertions.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/9830
Test Plan: make check
Reviewed By: ltamasi
Differential Revision: D35561162
Pulled By: riversand963
fbshipit-source-id: 0e00d200c195240341cfe6d34cbc86798b315b9f
2022-04-14 18:11:04 +00:00
|
|
|
SequenceNumber GetJobSnapshotSequence() const {
|
|
|
|
if (job_snapshot) {
|
|
|
|
assert(job_snapshot->snapshot());
|
|
|
|
return job_snapshot->snapshot()->GetSequenceNumber();
|
|
|
|
}
|
|
|
|
return kMaxSequenceNumber;
|
|
|
|
}
|
|
|
|
|
2014-10-28 18:54:33 +00:00
|
|
|
// Structure to store information for candidate files to delete.
|
|
|
|
struct CandidateFileInfo {
|
|
|
|
std::string file_name;
|
2018-04-06 02:49:06 +00:00
|
|
|
std::string file_path;
|
|
|
|
CandidateFileInfo(std::string name, std::string path)
|
2018-05-09 17:04:55 +00:00
|
|
|
: file_name(std::move(name)), file_path(std::move(path)) {}
|
2014-10-28 18:54:33 +00:00
|
|
|
bool operator==(const CandidateFileInfo& other) const {
|
2022-11-02 21:34:24 +00:00
|
|
|
return file_name == other.file_name && file_path == other.file_path;
|
2014-10-28 18:54:33 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-02-12 17:54:48 +00:00
|
|
|
// Unique job id
|
|
|
|
int job_id;
|
|
|
|
|
2014-10-28 18:54:33 +00:00
|
|
|
// a list of all files that we'll consider deleting
|
|
|
|
// (every once in a while this is filled up with all files
|
|
|
|
// in the DB directory)
|
2014-12-22 11:04:45 +00:00
|
|
|
// (filled only if we're doing full scan)
|
|
|
|
std::vector<CandidateFileInfo> full_scan_candidate_files;
|
2014-10-28 18:54:33 +00:00
|
|
|
|
|
|
|
// the list of all live sst files that cannot be deleted
|
2020-05-04 22:05:34 +00:00
|
|
|
std::vector<uint64_t> sst_live;
|
2014-10-28 18:54:33 +00:00
|
|
|
|
2020-05-04 22:05:34 +00:00
|
|
|
// the list of sst files that we need to delete
|
2018-04-06 02:49:06 +00:00
|
|
|
std::vector<ObsoleteFileInfo> sst_delete_files;
|
2014-10-28 18:54:33 +00:00
|
|
|
|
2020-05-04 22:05:34 +00:00
|
|
|
// the list of all live blob files that cannot be deleted
|
|
|
|
std::vector<uint64_t> blob_live;
|
|
|
|
|
2020-04-30 18:23:32 +00:00
|
|
|
// the list of blob files that we need to delete
|
|
|
|
std::vector<ObsoleteBlobFileInfo> blob_delete_files;
|
|
|
|
|
2014-10-28 18:54:33 +00:00
|
|
|
// a list of log files that we need to delete
|
|
|
|
std::vector<uint64_t> log_delete_files;
|
2016-10-13 15:48:40 +00:00
|
|
|
|
|
|
|
// a list of log files that we need to preserve during full purge since they
|
|
|
|
// will be reused later
|
|
|
|
std::vector<uint64_t> log_recycle_files;
|
2014-10-28 18:54:33 +00:00
|
|
|
|
2023-11-11 16:11:11 +00:00
|
|
|
// Files quarantined from deletion. This list contains file numbers for files
|
|
|
|
// that are in an ambiguous states. This includes newly generated SST files
|
|
|
|
// and blob files from flush and compaction job whose VersionEdits' persist
|
|
|
|
// state in Manifest are unclear. An old manifest file whose immediately
|
|
|
|
// following new manifest file's CURRENT file creation is in an unclear state.
|
|
|
|
// WAL logs don't have this premature deletion risk since
|
|
|
|
// min_log_number_to_keep is only updated after successful manifest commits.
|
|
|
|
// So this data structure doesn't track log files.
|
|
|
|
autovector<uint64_t> files_to_quarantine;
|
|
|
|
|
2016-03-11 02:16:21 +00:00
|
|
|
// a list of manifest files that we need to delete
|
|
|
|
std::vector<std::string> manifest_delete_files;
|
|
|
|
|
2014-10-28 18:54:33 +00:00
|
|
|
// a list of memtables to be free
|
|
|
|
autovector<MemTable*> memtables_to_free;
|
|
|
|
|
2018-08-04 00:34:07 +00:00
|
|
|
// contexts for installing superversions for multiple column families
|
|
|
|
std::vector<SuperVersionContext> superversion_contexts;
|
2014-10-28 18:54:33 +00:00
|
|
|
|
2015-03-30 19:04:10 +00:00
|
|
|
autovector<log::Writer*> logs_to_free;
|
|
|
|
|
2014-10-28 18:54:33 +00:00
|
|
|
// the current manifest_file_number, log_number and prev_log_number
|
|
|
|
// that corresponds to the set of files in 'live'.
|
2014-11-07 19:50:34 +00:00
|
|
|
uint64_t manifest_file_number;
|
|
|
|
uint64_t pending_manifest_file_number;
|
|
|
|
uint64_t log_number;
|
|
|
|
uint64_t prev_log_number;
|
|
|
|
|
|
|
|
uint64_t min_pending_output = 0;
|
2016-06-28 00:42:14 +00:00
|
|
|
uint64_t prev_total_log_size = 0;
|
|
|
|
size_t num_alive_log_files = 0;
|
|
|
|
uint64_t size_log_to_delete = 0;
|
2014-10-28 18:54:33 +00:00
|
|
|
|
2019-01-16 05:32:15 +00:00
|
|
|
// Snapshot taken before flush/compaction job.
|
|
|
|
std::unique_ptr<ManagedSnapshot> job_snapshot;
|
|
|
|
|
2018-08-04 00:34:07 +00:00
|
|
|
explicit JobContext(int _job_id, bool create_superversion = false) {
|
2015-02-12 17:54:48 +00:00
|
|
|
job_id = _job_id;
|
2014-10-28 18:54:33 +00:00
|
|
|
manifest_file_number = 0;
|
|
|
|
pending_manifest_file_number = 0;
|
|
|
|
log_number = 0;
|
|
|
|
prev_log_number = 0;
|
2018-08-04 00:34:07 +00:00
|
|
|
superversion_contexts.emplace_back(
|
|
|
|
SuperVersionContext(create_superversion));
|
2014-10-28 18:54:33 +00:00
|
|
|
}
|
|
|
|
|
2015-07-07 19:10:10 +00:00
|
|
|
// For non-empty JobContext Clean() has to be called at least once before
|
|
|
|
// before destruction (see asserts in ~JobContext()). Should be called with
|
|
|
|
// unlocked DB mutex. Destructor doesn't call Clean() to avoid accidentally
|
|
|
|
// doing potentially slow Clean() with locked DB mutex.
|
2014-11-14 23:43:10 +00:00
|
|
|
void Clean() {
|
2017-10-06 01:00:38 +00:00
|
|
|
// free superversions
|
2018-08-04 00:34:07 +00:00
|
|
|
for (auto& sv_context : superversion_contexts) {
|
|
|
|
sv_context.Clean();
|
|
|
|
}
|
2014-10-28 18:54:33 +00:00
|
|
|
// free pending memtables
|
|
|
|
for (auto m : memtables_to_free) {
|
|
|
|
delete m;
|
|
|
|
}
|
2015-03-30 19:04:10 +00:00
|
|
|
for (auto l : logs_to_free) {
|
|
|
|
delete l;
|
|
|
|
}
|
2014-11-14 23:43:10 +00:00
|
|
|
|
|
|
|
memtables_to_free.clear();
|
2015-03-30 19:04:10 +00:00
|
|
|
logs_to_free.clear();
|
2019-01-16 05:32:15 +00:00
|
|
|
job_snapshot.reset();
|
2014-11-14 23:43:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~JobContext() {
|
|
|
|
assert(memtables_to_free.size() == 0);
|
2015-07-07 19:10:10 +00:00
|
|
|
assert(logs_to_free.size() == 0);
|
2014-10-28 18:54:33 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
} // namespace ROCKSDB_NAMESPACE
|