1 //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 
6 #pragma once
7 
8 #ifndef ROCKSDB_LITE
9 
10 #include <string>
11 #include <vector>
12 #include "db/db_impl/db_impl.h"
13 
14 namespace ROCKSDB_NAMESPACE {
15 
16 // A wrapper class to hold log reader, log reporter, log status.
17 class LogReaderContainer {
18  public:
LogReaderContainer()19   LogReaderContainer()
20       : reader_(nullptr), reporter_(nullptr), status_(nullptr) {}
LogReaderContainer(Env * env,std::shared_ptr<Logger> info_log,std::string fname,std::unique_ptr<SequentialFileReader> && file_reader,uint64_t log_number)21   LogReaderContainer(Env* env, std::shared_ptr<Logger> info_log,
22                      std::string fname,
23                      std::unique_ptr<SequentialFileReader>&& file_reader,
24                      uint64_t log_number) {
25     LogReporter* reporter = new LogReporter();
26     status_ = new Status();
27     reporter->env = env;
28     reporter->info_log = info_log.get();
29     reporter->fname = std::move(fname);
30     reporter->status = status_;
31     reporter_ = reporter;
32     // We intentially make log::Reader do checksumming even if
33     // paranoid_checks==false so that corruptions cause entire commits
34     // to be skipped instead of propagating bad information (like overly
35     // large sequence numbers).
36     reader_ = new log::FragmentBufferedReader(info_log, std::move(file_reader),
37                                               reporter, true /*checksum*/,
38                                               log_number);
39   }
40   log::FragmentBufferedReader* reader_;
41   log::Reader::Reporter* reporter_;
42   Status* status_;
~LogReaderContainer()43   ~LogReaderContainer() {
44     delete reader_;
45     delete reporter_;
46     delete status_;
47   }
48  private:
49   struct LogReporter : public log::Reader::Reporter {
50     Env* env;
51     Logger* info_log;
52     std::string fname;
53     Status* status;  // nullptr if immutable_db_options_.paranoid_checks==false
CorruptionLogReporter54     void Corruption(size_t bytes, const Status& s) override {
55       ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
56                      (this->status == nullptr ? "(ignoring error) " : ""),
57                      fname.c_str(), static_cast<int>(bytes),
58                      s.ToString().c_str());
59       if (this->status != nullptr && this->status->ok()) {
60         *this->status = s;
61       }
62     }
63   };
64 };
65 
66 // The secondary instance shares access to the storage as the primary.
67 // The secondary is able to read and replay changes described in both the
68 // MANIFEST and the WAL files without coordination with the primary.
69 // The secondary instance can be opened using `DB::OpenAsSecondary`. After
70 // that, it can call `DBImplSecondary::TryCatchUpWithPrimary` to make best
71 // effort attempts to catch up with the primary.
72 class DBImplSecondary : public DBImpl {
73  public:
74   DBImplSecondary(const DBOptions& options, const std::string& dbname);
75   ~DBImplSecondary() override;
76 
77   // Recover by replaying MANIFEST and WAL. Also initialize manifest_reader_
78   // and log_readers_ to facilitate future operations.
79   Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families,
80                  bool read_only, bool error_if_log_file_exist,
81                  bool error_if_data_exists_in_logs,
82                  uint64_t* = nullptr) override;
83 
84   // Implementations of the DB interface
85   using DB::Get;
86   Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
87              const Slice& key, PinnableSlice* value) override;
88 
89   Status GetImpl(const ReadOptions& options, ColumnFamilyHandle* column_family,
90                  const Slice& key, PinnableSlice* value);
91 
92   using DBImpl::NewIterator;
93   Iterator* NewIterator(const ReadOptions&,
94                         ColumnFamilyHandle* column_family) override;
95 
96   ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& read_options,
97                                       ColumnFamilyData* cfd,
98                                       SequenceNumber snapshot,
99                                       ReadCallback* read_callback);
100 
101   Status NewIterators(const ReadOptions& options,
102                       const std::vector<ColumnFamilyHandle*>& column_families,
103                       std::vector<Iterator*>* iterators) override;
104 
105   using DBImpl::Put;
Put(const WriteOptions &,ColumnFamilyHandle *,const Slice &,const Slice &)106   Status Put(const WriteOptions& /*options*/,
107              ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
108              const Slice& /*value*/) override {
109     return Status::NotSupported("Not supported operation in secondary mode.");
110   }
111 
112   using DBImpl::Merge;
Merge(const WriteOptions &,ColumnFamilyHandle *,const Slice &,const Slice &)113   Status Merge(const WriteOptions& /*options*/,
114                ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
115                const Slice& /*value*/) override {
116     return Status::NotSupported("Not supported operation in secondary mode.");
117   }
118 
119   using DBImpl::Delete;
Delete(const WriteOptions &,ColumnFamilyHandle *,const Slice &)120   Status Delete(const WriteOptions& /*options*/,
121                 ColumnFamilyHandle* /*column_family*/,
122                 const Slice& /*key*/) override {
123     return Status::NotSupported("Not supported operation in secondary mode.");
124   }
125 
126   using DBImpl::SingleDelete;
SingleDelete(const WriteOptions &,ColumnFamilyHandle *,const Slice &)127   Status SingleDelete(const WriteOptions& /*options*/,
128                       ColumnFamilyHandle* /*column_family*/,
129                       const Slice& /*key*/) override {
130     return Status::NotSupported("Not supported operation in secondary mode.");
131   }
132 
Write(const WriteOptions &,WriteBatch *)133   Status Write(const WriteOptions& /*options*/,
134                WriteBatch* /*updates*/) override {
135     return Status::NotSupported("Not supported operation in secondary mode.");
136   }
137 
138   using DBImpl::CompactRange;
CompactRange(const CompactRangeOptions &,ColumnFamilyHandle *,const Slice *,const Slice *)139   Status CompactRange(const CompactRangeOptions& /*options*/,
140                       ColumnFamilyHandle* /*column_family*/,
141                       const Slice* /*begin*/, const Slice* /*end*/) override {
142     return Status::NotSupported("Not supported operation in secondary mode.");
143   }
144 
145   using DBImpl::CompactFiles;
146   Status CompactFiles(
147       const CompactionOptions& /*compact_options*/,
148       ColumnFamilyHandle* /*column_family*/,
149       const std::vector<std::string>& /*input_file_names*/,
150       const int /*output_level*/, const int /*output_path_id*/ = -1,
151       std::vector<std::string>* const /*output_file_names*/ = nullptr,
152       CompactionJobInfo* /*compaction_job_info*/ = nullptr) override {
153     return Status::NotSupported("Not supported operation in secondary mode.");
154   }
155 
DisableFileDeletions()156   Status DisableFileDeletions() override {
157     return Status::NotSupported("Not supported operation in secondary mode.");
158   }
159 
EnableFileDeletions(bool)160   Status EnableFileDeletions(bool /*force*/) override {
161     return Status::NotSupported("Not supported operation in secondary mode.");
162   }
163 
164   Status GetLiveFiles(std::vector<std::string>&,
165                       uint64_t* /*manifest_file_size*/,
166                       bool /*flush_memtable*/ = true) override {
167     return Status::NotSupported("Not supported operation in secondary mode.");
168   }
169 
170   using DBImpl::Flush;
Flush(const FlushOptions &,ColumnFamilyHandle *)171   Status Flush(const FlushOptions& /*options*/,
172                ColumnFamilyHandle* /*column_family*/) override {
173     return Status::NotSupported("Not supported operation in secondary mode.");
174   }
175 
176   using DBImpl::SetDBOptions;
SetDBOptions(const std::unordered_map<std::string,std::string> &)177   Status SetDBOptions(const std::unordered_map<std::string, std::string>&
178                       /*options_map*/) override {
179     // Currently not supported because changing certain options may cause
180     // flush/compaction.
181     return Status::NotSupported("Not supported operation in secondary mode.");
182   }
183 
184   using DBImpl::SetOptions;
SetOptions(ColumnFamilyHandle *,const std::unordered_map<std::string,std::string> &)185   Status SetOptions(
186       ColumnFamilyHandle* /*cfd*/,
187       const std::unordered_map<std::string, std::string>& /*options_map*/)
188       override {
189     // Currently not supported because changing certain options may cause
190     // flush/compaction and/or write to MANIFEST.
191     return Status::NotSupported("Not supported operation in secondary mode.");
192   }
193 
194   using DBImpl::SyncWAL;
SyncWAL()195   Status SyncWAL() override {
196     return Status::NotSupported("Not supported operation in secondary mode.");
197   }
198 
199   using DB::IngestExternalFile;
IngestExternalFile(ColumnFamilyHandle *,const std::vector<std::string> &,const IngestExternalFileOptions &)200   Status IngestExternalFile(
201       ColumnFamilyHandle* /*column_family*/,
202       const std::vector<std::string>& /*external_files*/,
203       const IngestExternalFileOptions& /*ingestion_options*/) override {
204     return Status::NotSupported("Not supported operation in secondary mode.");
205   }
206 
207   // Try to catch up with the primary by reading as much as possible from the
208   // log files until there is nothing more to read or encounters an error. If
209   // the amount of information in the log files to process is huge, this
210   // method can take long time due to all the I/O and CPU costs.
211   Status TryCatchUpWithPrimary() override;
212 
213 
214   // Try to find log reader using log_number from log_readers_ map, initialize
215   // if it doesn't exist
216   Status MaybeInitLogReader(uint64_t log_number,
217                             log::FragmentBufferedReader** log_reader);
218 
219   // Check if all live files exist on file system and that their file sizes
220   // matche to the in-memory records. It is possible that some live files may
221   // have been deleted by the primary. In this case, CheckConsistency() does
222   // not flag the missing file as inconsistency.
223   Status CheckConsistency() override;
224 
225  protected:
226   // ColumnFamilyCollector is a write batch handler which does nothing
227   // except recording unique column family IDs
228   class ColumnFamilyCollector : public WriteBatch::Handler {
229     std::unordered_set<uint32_t> column_family_ids_;
230 
AddColumnFamilyId(uint32_t column_family_id)231     Status AddColumnFamilyId(uint32_t column_family_id) {
232       if (column_family_ids_.find(column_family_id) ==
233           column_family_ids_.end()) {
234         column_family_ids_.insert(column_family_id);
235       }
236       return Status::OK();
237     }
238 
239    public:
ColumnFamilyCollector()240     explicit ColumnFamilyCollector() {}
241 
~ColumnFamilyCollector()242     ~ColumnFamilyCollector() override {}
243 
PutCF(uint32_t column_family_id,const Slice &,const Slice &)244     Status PutCF(uint32_t column_family_id, const Slice&,
245                  const Slice&) override {
246       return AddColumnFamilyId(column_family_id);
247     }
248 
DeleteCF(uint32_t column_family_id,const Slice &)249     Status DeleteCF(uint32_t column_family_id, const Slice&) override {
250       return AddColumnFamilyId(column_family_id);
251     }
252 
SingleDeleteCF(uint32_t column_family_id,const Slice &)253     Status SingleDeleteCF(uint32_t column_family_id, const Slice&) override {
254       return AddColumnFamilyId(column_family_id);
255     }
256 
DeleteRangeCF(uint32_t column_family_id,const Slice &,const Slice &)257     Status DeleteRangeCF(uint32_t column_family_id, const Slice&,
258                          const Slice&) override {
259       return AddColumnFamilyId(column_family_id);
260     }
261 
MergeCF(uint32_t column_family_id,const Slice &,const Slice &)262     Status MergeCF(uint32_t column_family_id, const Slice&,
263                    const Slice&) override {
264       return AddColumnFamilyId(column_family_id);
265     }
266 
PutBlobIndexCF(uint32_t column_family_id,const Slice &,const Slice &)267     Status PutBlobIndexCF(uint32_t column_family_id, const Slice&,
268                           const Slice&) override {
269       return AddColumnFamilyId(column_family_id);
270     }
271 
column_families()272     const std::unordered_set<uint32_t>& column_families() const {
273       return column_family_ids_;
274     }
275   };
276 
CollectColumnFamilyIdsFromWriteBatch(const WriteBatch & batch,std::vector<uint32_t> * column_family_ids)277   Status CollectColumnFamilyIdsFromWriteBatch(
278       const WriteBatch& batch, std::vector<uint32_t>* column_family_ids) {
279     assert(column_family_ids != nullptr);
280     column_family_ids->clear();
281     ColumnFamilyCollector handler;
282     Status s = batch.Iterate(&handler);
283     if (s.ok()) {
284       for (const auto& cf : handler.column_families()) {
285         column_family_ids->push_back(cf);
286       }
287     }
288     return s;
289   }
290 
OwnTablesAndLogs()291   bool OwnTablesAndLogs() const override {
292     // Currently, the secondary instance does not own the database files. It
293     // simply opens the files of the primary instance and tracks their file
294     // descriptors until they become obsolete. In the future, the secondary may
295     // create links to database files. OwnTablesAndLogs will return true then.
296     return false;
297   }
298 
299  private:
300   friend class DB;
301 
302   // No copying allowed
303   DBImplSecondary(const DBImplSecondary&);
304   void operator=(const DBImplSecondary&);
305 
306   using DBImpl::Recover;
307 
308   Status FindAndRecoverLogFiles(
309       std::unordered_set<ColumnFamilyData*>* cfds_changed,
310       JobContext* job_context);
311   Status FindNewLogNumbers(std::vector<uint64_t>* logs);
312   // After manifest recovery, replay WALs and refresh log_readers_ if necessary
313   // REQUIRES: log_numbers are sorted in ascending order
314   Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
315                          SequenceNumber* next_sequence,
316                          std::unordered_set<ColumnFamilyData*>* cfds_changed,
317                          JobContext* job_context);
318 
319   std::unique_ptr<log::FragmentBufferedReader> manifest_reader_;
320   std::unique_ptr<log::Reader::Reporter> manifest_reporter_;
321   std::unique_ptr<Status> manifest_reader_status_;
322 
323   // Cache log readers for each log number, used for continue WAL replay
324   // after recovery
325   std::map<uint64_t, std::unique_ptr<LogReaderContainer>> log_readers_;
326 
327   // Current WAL number replayed for each column family.
328   std::unordered_map<ColumnFamilyData*, uint64_t> cfd_to_current_log_;
329 };
330 
331 }  // namespace ROCKSDB_NAMESPACE
332 
333 #endif  // !ROCKSDB_LITE
334