db.hpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017
  1. /*************************************************************************
  2. *
  3. * Copyright 2016 Realm Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. **************************************************************************/
  18. #ifndef REALM_GROUP_SHARED_HPP
  19. #define REALM_GROUP_SHARED_HPP
  20. #include <functional>
  21. #include <cstdint>
  22. #include <limits>
  23. #include <realm/util/features.h>
  24. #include <realm/util/thread.hpp>
  25. #include <realm/util/interprocess_condvar.hpp>
  26. #include <realm/util/interprocess_mutex.hpp>
  27. #include <realm/group.hpp>
  28. #include <realm/handover_defs.hpp>
  29. #include <realm/impl/transact_log.hpp>
  30. #include <realm/metrics/metrics.hpp>
  31. #include <realm/replication.hpp>
  32. #include <realm/version_id.hpp>
  33. #include <realm/db_options.hpp>
  34. #include <realm/util/logger.hpp>
  35. namespace realm {
  36. namespace _impl {
  37. class WriteLogCollector;
  38. }
  39. class Transaction;
  40. using TransactionRef = std::shared_ptr<Transaction>;
  41. /// Thrown by DB::create() if the lock file is already open in another
  42. /// process which can't share mutexes with this process
  43. struct IncompatibleLockFile : std::runtime_error {
  44. IncompatibleLockFile(const std::string& msg)
  45. : std::runtime_error("Incompatible lock file. " + msg)
  46. {
  47. }
  48. };
  49. /// Thrown by DB::create() if the type of history
  50. /// (Replication::HistoryType) in the opened Realm file is incompatible with the
  51. /// mode in which the Realm file is opened. For example, if there is a mismatch
  52. /// between the history type in the file, and the history type associated with
  53. /// the replication plugin passed to DB::create().
  54. ///
  55. /// This exception will also be thrown if the history schema version is lower
  56. /// than required, and no migration is possible
  57. /// (Replication::is_upgradable_history_schema()).
  58. struct IncompatibleHistories : util::File::AccessError {
  59. IncompatibleHistories(const std::string& msg, const std::string& path)
  60. : util::File::AccessError("Incompatible histories. " + msg, path)
  61. {
  62. }
  63. };
  64. /// The FileFormatUpgradeRequired exception can be thrown by the DB
  65. /// constructor when opening a database that uses a deprecated file format
  66. /// and/or a deprecated history schema, and the user has indicated he does not
  67. /// want automatic upgrades to be performed. This exception indicates that until
  68. /// an upgrade of the file format is performed, the database will be unavailable
  69. /// for read or write operations.
  70. /// It will also be thrown if a realm which requires upgrade is opened in read-only
  71. /// mode (Group::open).
  72. struct FileFormatUpgradeRequired : util::File::AccessError {
  73. FileFormatUpgradeRequired(const std::string& msg, const std::string& path)
  74. : util::File::AccessError(msg, path)
  75. {
  76. }
  77. };
  78. /// A DB facilitates transactions.
  79. ///
  80. /// Access to a database is done through transactions. Transactions
  81. /// are created by a DB object. No matter how many transactions you
  82. /// use, you only need a single DB object per file. Methods on the DB
  83. /// object are thread-safe.
  84. ///
  85. /// Realm has 3 types of Transactions:
  86. /// * A frozen transaction allows read only access
  87. /// * A read transaction allows read only access but can be promoted
  88. /// to a write transaction.
  89. /// * A write transaction allows write access. A write transaction can
  90. /// be demoted to a read transaction.
  91. ///
  92. /// Frozen transactions are thread safe. Read and write transactions are not.
  93. ///
  94. /// Two processes that want to share a database file must reside on
  95. /// the same host.
  96. ///
  97. class DB;
  98. using DBRef = std::shared_ptr<DB>;
  99. class DB : public std::enable_shared_from_this<DB> {
  100. public:
  101. // Create a DB and associate it with a file. DB Objects can only be associated with one file,
  102. // the association determined on creation of the DB Object. The association can be broken by
  103. // calling DB::close(), but after that no new association can be established. To reopen the
  104. // file (or another file), a new DB object is needed.
  105. static DBRef create(const std::string& file, bool no_create = false, const DBOptions options = DBOptions());
  106. static DBRef create(Replication& repl, const DBOptions options = DBOptions());
  107. static DBRef create(BinaryData, bool take_ownership = true);
  108. ~DB() noexcept;
  109. // Disable copying to prevent accessor errors. If you really want another
  110. // instance, open another DB object on the same file. But you don't.
  111. DB(const DB&) = delete;
  112. DB& operator=(const DB&) = delete;
  113. /// Close an open database. Calling close() is thread-safe with respect to
  114. /// other calls to close and with respect to deleting transactions.
  115. /// Calling close() while a write transaction is open is an error and close()
  116. /// will throw a LogicError::wrong_transact_state.
  117. /// Calling close() while a read transaction is open is by default treated
  118. /// in the same way, but close(true) will allow the error to be ignored and
  119. /// release resources despite open read transactions.
  120. /// As successfull call to close() leaves transactions (and any associated
  121. /// accessors) in a defunct state and the actual close() operation is not
  122. /// interlocked with access through those accessors, so any access through accessors
  123. /// may constitute a race with a call to close().
  124. /// Instead of using DB::close() to release resources, we recommend using transactions
  125. /// to control release as follows:
  126. /// * explicitly nullify TransactionRefs at earliest time possible and
  127. /// * for read or write transactions - but not frozen transactions, explicitly call
  128. /// close() at earliest time possible
  129. /// * explicitly nullify any DBRefs you may have.
  130. void close(bool allow_open_read_transactions = false);
  131. bool is_attached() const noexcept;
  132. Allocator& get_alloc()
  133. {
  134. return m_alloc;
  135. }
  136. Replication* get_replication() const
  137. {
  138. return m_replication;
  139. }
  140. void set_replication(Replication* repl) noexcept
  141. {
  142. m_replication = repl;
  143. }
  144. #ifdef REALM_DEBUG
  145. /// Deprecated method, only called from a unit test
  146. ///
  147. /// Reserve disk space now to avoid allocation errors at a later
  148. /// point in time, and to minimize on-disk fragmentation. In some
  149. /// cases, less fragmentation translates into improved
  150. /// performance.
  151. ///
  152. /// When supported by the system, a call to this function will
  153. /// make the database file at least as big as the specified size,
  154. /// and cause space on the target device to be allocated (note
  155. /// that on many systems on-disk allocation is done lazily by
  156. /// default). If the file is already bigger than the specified
  157. /// size, the size will be unchanged, and on-disk allocation will
  158. /// occur only for the initial section that corresponds to the
  159. /// specified size.
  160. ///
  161. /// It is an error to call this function on an unattached shared
  162. /// group. Doing so will result in undefined behavior.
  163. void reserve(size_t size_in_bytes);
  164. #endif
  165. /// Querying for changes:
  166. ///
  167. /// NOTE:
  168. /// "changed" means that one or more commits has been made to the database
  169. /// since the presented transaction was made.
  170. ///
  171. /// No distinction is made between changes done by another process
  172. /// and changes done by another thread in the same process as the caller.
  173. ///
  174. /// Has db been changed ?
  175. bool has_changed(TransactionRef);
  176. /// The calling thread goes to sleep until the database is changed, or
  177. /// until wait_for_change_release() is called. After a call to
  178. /// wait_for_change_release() further calls to wait_for_change() will return
  179. /// immediately. To restore the ability to wait for a change, a call to
  180. /// enable_wait_for_change() is required. Return true if the database has
  181. /// changed, false if it might have.
  182. bool wait_for_change(TransactionRef);
  183. /// release any thread waiting in wait_for_change().
  184. void wait_for_change_release();
  185. /// re-enable waiting for change
  186. void enable_wait_for_change();
  187. // Transactions:
  188. using version_type = _impl::History::version_type;
  189. using VersionID = realm::VersionID;
  190. /// Returns the version of the latest snapshot.
  191. version_type get_version_of_latest_snapshot();
  192. VersionID get_version_id_of_latest_snapshot();
  193. /// Thrown by start_read() if the specified version does not correspond to a
  194. /// bound (AKA tethered) snapshot.
  195. struct BadVersion;
  196. /// Transactions are obtained from one of the following 3 methods:
  197. TransactionRef start_read(VersionID = VersionID());
  198. TransactionRef start_frozen(VersionID = VersionID());
  199. // If nonblocking is true and a write transaction is already active,
  200. // an invalid TransactionRef is returned.
  201. TransactionRef start_write(bool nonblocking = false);
  202. // report statistics of last commit done on THIS DB.
  203. // The free space reported is what can be expected to be freed
  204. // by compact(). This may not correspond to the space which is free
  205. // at the point where get_stats() is called, since that will include
  206. // memory required to hold older versions of data, which still
  207. // needs to be available. The locked space is the amount of memory
  208. // that is free in current version, but being used in still live versions.
  209. // Notice that we will always have two live versions - the current and the
  210. // previous.
  211. void get_stats(size_t& free_space, size_t& used_space, util::Optional<size_t&> locked_space = util::none) const;
  212. //@}
  213. enum TransactStage {
  214. transact_Ready,
  215. transact_Reading,
  216. transact_Writing,
  217. transact_Frozen,
  218. };
  219. /// Report the number of distinct versions currently stored in the database.
  220. /// Note: the database only cleans up versions as part of commit, so ending
  221. /// a read transaction will not immediately release any versions.
  222. uint_fast64_t get_number_of_versions();
  223. /// Get the size of the currently allocated slab area
  224. size_t get_allocated_size() const;
  225. /// Compact the database file.
  226. /// - The method will throw if called inside a transaction.
  227. /// - The method will throw if called in unattached state.
  228. /// - The method will return false if other DBs are accessing the
  229. /// database in which case compaction is not done. This is not
  230. /// necessarily an error.
  231. /// It will return true following successful compaction.
  232. /// While compaction is in progress, attempts by other
  233. /// threads or processes to open the database will wait.
  234. /// Likewise, attempts to create new transactions will wait.
  235. /// Be warned that resource requirements for compaction is proportional to
  236. /// the amount of live data in the database.
  237. /// Compaction works by writing the database contents to a temporary
  238. /// database file and then replacing the database with the temporary one.
  239. /// The name of the temporary file is formed by appending
  240. /// ".tmp_compaction_space" to the name of the database
  241. ///
  242. /// If the output_encryption_key is `none` then the file's existing key will
  243. /// be used (if any). If the output_encryption_key is nullptr, the resulting
  244. /// file will be unencrypted. Any other value will change the encryption of
  245. /// the file to the new 64 byte key.
  246. ///
  247. /// WARNING: Compact() is not thread-safe with respect to a concurrent close()
  248. bool compact(bool bump_version_number = false, util::Optional<const char*> output_encryption_key = util::none);
  249. void write_copy(StringData path, util::Optional<const char*> output_encryption_key = util::none,
  250. bool allow_overwrite = false);
  251. #ifdef REALM_DEBUG
  252. void test_ringbuf();
  253. #endif
  254. /// The relation between accessors, threads and the Transaction object.
  255. ///
  256. /// Once created, accessors belong to a transaction and can only be used for
  257. /// access as long as that transaction is still active. Copies of accessors
  258. /// can be created in association with another transaction, the importing transaction,
  259. /// using said transactions import_copy_of() method. This process is called
  260. /// accessor import. Prior to Core 6, the corresponding mechanism was known
  261. /// as "handover".
  262. ///
  263. /// For TableViews, there are 3 forms of import determined by the PayloadPolicy.
  264. ///
  265. /// - with payload move: the payload imported ends up as a payload
  266. /// held by the accessor at the importing side. The accessor on the
  267. /// exporting side will rerun its query and generate a new payload, if
  268. /// TableView::sync_if_needed() is called. If the original payload was in
  269. /// sync at the exporting side, it will also be in sync at the importing
  270. /// side. This policy is selected by PayloadPolicy::Move
  271. ///
  272. /// - with payload copy: a copy of the payload is imported, so both the
  273. /// accessors on the exporting side *and* the accessors created at the
  274. /// importing side has their own payload. This is policy is selected
  275. /// by PayloadPolicy::Copy
  276. ///
  277. /// - without payload: the payload stays with the accessor on the exporting
  278. /// side. On the importing side, the new accessor is created without
  279. /// payload. A call to TableView::sync_if_needed() will trigger generation
  280. /// of a new payload. This policy is selected by PayloadPolicy::Stay.
  281. ///
  282. /// For all other (non-TableView) accessors, importing is done with payload
  283. /// copy, since the payload is trivial.
  284. ///
  285. /// Importing *without* payload is useful when you want to ship a tableview
  286. /// with its query for execution in a background thread. Handover with
  287. /// *payload move* is useful when you want to transfer the result back.
  288. ///
  289. /// Importing *without* payload or with payload copy is guaranteed *not* to
  290. /// change the accessors on the exporting side.
  291. ///
  292. /// Importing is generally *not* thread safe and should be carried out
  293. /// by the thread that "owns" the involved accessors. However, importing
  294. /// *is* thread-safe when it occurs from a *frozen* accessor.
  295. ///
  296. /// Importing is transitive:
  297. /// If the object being imported depends on other views
  298. /// (table- or link- ), those objects will be imported as well. The mode
  299. /// (payload copy, payload move, without payload) is applied
  300. /// recursively. Note: If you are importing a tableview dependent upon
  301. /// another tableview and using MutableSourcePayload::Move,
  302. /// you are on thin ice!
  303. ///
  304. /// On the importing side, the top-level accessor being created during
  305. /// import takes ownership of all other accessors (if any) being created as
  306. /// part of the import.
  307. std::shared_ptr<metrics::Metrics> get_metrics()
  308. {
  309. return m_metrics;
  310. }
  311. // Try to grab a exclusive lock of the given realm path's lock file. If the lock
  312. // can be acquired, the callback will be executed with the lock and then return true.
  313. // Otherwise false will be returned directly.
  314. // The lock taken precludes races with other threads or processes accessing the
  315. // files through a DB.
  316. // It is safe to delete/replace realm files inside the callback.
  317. // WARNING: It is not safe to delete the lock file in the callback.
  318. using CallbackWithLock = std::function<void(const std::string& realm_path)>;
  319. static bool call_with_lock(const std::string& realm_path, CallbackWithLock callback);
  320. // Return a list of files/directories core may use of the given realm file path.
  321. // The first element of the pair in the returned list is the path string, the
  322. // second one is to indicate the path is a directory or not.
  323. // The temporary files are not returned by this function.
  324. // It is safe to delete those returned files/directories in the call_with_lock's callback.
  325. static std::vector<std::pair<std::string, bool>> get_core_files(const std::string& realm_path);
  326. protected:
  327. explicit DB(const DBOptions& options); // Is this ever used?
  328. private:
  329. std::recursive_mutex m_mutex;
  330. int m_transaction_count = 0;
  331. SlabAlloc m_alloc;
  332. Replication* m_replication = nullptr;
  333. struct SharedInfo;
  334. struct ReadCount;
  335. struct ReadLockInfo {
  336. uint_fast64_t m_version = std::numeric_limits<version_type>::max();
  337. uint_fast32_t m_reader_idx = 0;
  338. ref_type m_top_ref = 0;
  339. size_t m_file_size = 0;
  340. // a little helper
  341. static std::unique_ptr<ReadLockInfo> make_fake(ref_type top_ref, size_t file_size)
  342. {
  343. auto res = std::make_unique<ReadLockInfo>();
  344. res->m_top_ref = top_ref;
  345. res->m_file_size = file_size;
  346. return res;
  347. }
  348. };
  349. class ReadLockGuard;
  350. // Member variables
  351. size_t m_free_space = 0;
  352. size_t m_locked_space = 0;
  353. size_t m_used_space = 0;
  354. uint_fast32_t m_local_max_entry = 0; // highest version observed by this DB
  355. std::vector<ReadLockInfo> m_local_locks_held; // tracks all read locks held by this DB
  356. util::File m_file;
  357. util::File::Map<SharedInfo> m_file_map; // Never remapped, provides access to everything but the ringbuffer
  358. util::File::Map<SharedInfo> m_reader_map; // provides access to ringbuffer, remapped as needed when it grows
  359. bool m_wait_for_change_enabled = true; // Initially wait_for_change is enabled
  360. bool m_write_transaction_open = false;
  361. std::string m_lockfile_path;
  362. std::string m_lockfile_prefix;
  363. std::string m_db_path;
  364. std::string m_coordination_dir;
  365. const char* m_key;
  366. int m_file_format_version = 0;
  367. util::InterprocessMutex m_writemutex;
  368. std::unique_ptr<ReadLockInfo> m_fake_read_lock_if_immutable;
  369. #ifdef REALM_ASYNC_DAEMON
  370. util::InterprocessMutex m_balancemutex;
  371. #endif
  372. util::InterprocessMutex m_controlmutex;
  373. #ifdef REALM_ASYNC_DAEMON
  374. util::InterprocessCondVar m_room_to_write;
  375. util::InterprocessCondVar m_work_to_do;
  376. util::InterprocessCondVar m_daemon_becomes_ready;
  377. #endif
  378. util::InterprocessCondVar m_new_commit_available;
  379. util::InterprocessCondVar m_pick_next_writer;
  380. std::function<void(int, int)> m_upgrade_callback;
  381. std::shared_ptr<metrics::Metrics> m_metrics;
  382. /// Attach this DB instance to the specified database file.
  383. ///
  384. /// While at least one instance of DB exists for a specific
  385. /// database file, a "lock" file will be present too. The lock file will be
  386. /// placed in the same directory as the database file, and its name will be
  387. /// derived by appending ".lock" to the name of the database file.
  388. ///
  389. /// When multiple DB instances refer to the same file, they must
  390. /// specify the same durability level, otherwise an exception will be
  391. /// thrown.
  392. ///
  393. /// \param file Filesystem path to a Realm database file.
  394. ///
  395. /// \param no_create If the database file does not already exist, it will be
  396. /// created (unless this is set to true.) When multiple threads are involved,
  397. /// it is safe to let the first thread, that gets to it, create the file.
  398. ///
  399. /// \param options See DBOptions for details of each option.
  400. /// Sensible defaults are provided if this parameter is left out.
  401. ///
  402. /// \throw util::File::AccessError If the file could not be opened. If the
  403. /// reason corresponds to one of the exception types that are derived from
  404. /// util::File::AccessError, the derived exception type is thrown. Note that
  405. /// InvalidDatabase is among these derived exception types.
  406. ///
  407. /// \throw FileFormatUpgradeRequired if \a DBOptions::allow_upgrade
  408. /// is `false` and an upgrade is required.
  409. ///
  410. /// \throw LogicError if both DBOptions::allow_upgrade and is_immutable is true.
  411. /// \throw UnsupportedFileFormatVersion if the file format version or
  412. /// history schema version is one which this version of Realm does not know
  413. /// how to migrate from.
  414. void open(const std::string& file, bool no_create = false, const DBOptions options = DBOptions());
  415. void open(BinaryData, bool take_ownership = true);
  416. /// Open this group in replication mode. The specified Replication instance
  417. /// must remain in existence for as long as the DB.
  418. void open(Replication&, const DBOptions options = DBOptions());
  419. void do_open(const std::string& file, bool no_create, bool is_backend, const DBOptions options);
  420. Replication* const* get_repl() const noexcept
  421. {
  422. return &m_replication;
  423. }
  424. // Ring buffer management
  425. bool ringbuf_is_empty() const noexcept;
  426. size_t ringbuf_size() const noexcept;
  427. size_t ringbuf_capacity() const noexcept;
  428. bool ringbuf_is_first(size_t ndx) const noexcept;
  429. void ringbuf_remove_first() noexcept;
  430. size_t ringbuf_find(uint64_t version) const noexcept;
  431. ReadCount& ringbuf_get(size_t ndx) noexcept;
  432. ReadCount& ringbuf_get_first() noexcept;
  433. ReadCount& ringbuf_get_last() noexcept;
  434. void ringbuf_put(const ReadCount& v);
  435. void ringbuf_expand();
  436. /// Grab a read lock on the snapshot associated with the specified
  437. /// version. If `version_id == VersionID()`, a read lock will be grabbed on
  438. /// the latest available snapshot. Fails if the snapshot is no longer
  439. /// available.
  440. ///
  441. /// As a side effect update memory mapping to ensure that the ringbuffer
  442. /// entries referenced in the readlock info is accessible.
  443. void grab_read_lock(ReadLockInfo&, VersionID);
  444. // Release a specific read lock. The read lock MUST have been obtained by a
  445. // call to grab_read_lock().
  446. void release_read_lock(ReadLockInfo&) noexcept;
  447. // Release all read locks held by this DB object. After release, further calls to
  448. // release_read_lock for locks already released must be avoided.
  449. void release_all_read_locks() noexcept;
  450. /// return true if write transaction can commence, false otherwise.
  451. bool do_try_begin_write();
  452. void do_begin_write();
  453. version_type do_commit(Transaction&);
  454. void do_end_write() noexcept;
  455. // make sure the given index is within the currently mapped area.
  456. // if not, expand the mapped area. Returns true if the area is expanded.
  457. bool grow_reader_mapping(uint_fast32_t index);
  458. // Must be called only by someone that has a lock on the write mutex.
  459. void low_level_commit(uint_fast64_t new_version, Transaction& transaction);
  460. void do_async_commits();
  461. /// Upgrade file format and/or history schema
  462. void upgrade_file_format(bool allow_file_format_upgrade, int target_file_format_version,
  463. int current_hist_schema_version, int target_hist_schema_version);
  464. int get_file_format_version() const noexcept;
  465. /// finish up the process of starting a write transaction. Internal use only.
  466. void finish_begin_write();
  467. void reset_free_space_tracking()
  468. {
  469. m_alloc.reset_free_space_tracking();
  470. }
  471. void close_internal(std::unique_lock<util::InterprocessMutex>, bool allow_open_read_transactions);
  472. friend class Transaction;
  473. };
  474. inline void DB::get_stats(size_t& free_space, size_t& used_space, util::Optional<size_t&> locked_space) const
  475. {
  476. free_space = m_free_space;
  477. used_space = m_used_space;
  478. if (locked_space) {
  479. *locked_space = m_locked_space;
  480. }
  481. }
  482. class Transaction : public Group {
  483. public:
  484. Transaction(DBRef _db, SlabAlloc* alloc, DB::ReadLockInfo& rli, DB::TransactStage stage);
  485. // convenience, so you don't need to carry a reference to the DB around
  486. ~Transaction();
  487. DB::version_type get_version() const noexcept
  488. {
  489. return m_read_lock.m_version;
  490. }
  491. DB::version_type get_version_of_latest_snapshot()
  492. {
  493. return db->get_version_of_latest_snapshot();
  494. }
  495. void close();
  496. bool is_attached()
  497. {
  498. return m_transact_stage != DB::transact_Ready && db->is_attached();
  499. }
  500. /// Get the approximate size of the data that would be written to the file if
  501. /// a commit were done at this point. The reported size will always be bigger
  502. /// than what will eventually be needed as we reserve a bit more memory than
  503. /// what will be needed.
  504. size_t get_commit_size() const;
  505. DB::version_type commit();
  506. void rollback();
  507. void end_read();
  508. // Live transactions state changes, often taking an observer functor:
  509. VersionID commit_and_continue_as_read();
  510. template <class O>
  511. void rollback_and_continue_as_read(O* observer);
  512. void rollback_and_continue_as_read()
  513. {
  514. _impl::NullInstructionObserver* o = nullptr;
  515. rollback_and_continue_as_read(o);
  516. }
  517. template <class O>
  518. void advance_read(O* observer, VersionID target_version = VersionID());
  519. void advance_read(VersionID target_version = VersionID())
  520. {
  521. _impl::NullInstructionObserver* o = nullptr;
  522. advance_read(o, target_version);
  523. }
  524. template <class O>
  525. bool promote_to_write(O* observer, bool nonblocking = false);
  526. bool promote_to_write(bool nonblocking = false)
  527. {
  528. _impl::NullInstructionObserver* o = nullptr;
  529. return promote_to_write(o, nonblocking);
  530. }
  531. TransactionRef freeze();
  532. // Frozen transactions are created by freeze() or DB::start_frozen()
  533. bool is_frozen() const noexcept override
  534. {
  535. return m_transact_stage == DB::transact_Frozen;
  536. }
  537. TransactionRef duplicate();
  538. _impl::History* get_history() const;
  539. // direct handover of accessor instances
  540. Obj import_copy_of(const Obj& original);
  541. TableRef import_copy_of(const ConstTableRef original);
  542. LnkLst import_copy_of(const LnkLst& original);
  543. LnkSet import_copy_of(const LnkSet& original);
  544. LstBasePtr import_copy_of(const LstBase& original);
  545. SetBasePtr import_copy_of(const SetBase& original);
  546. CollectionBasePtr import_copy_of(const CollectionBase& original);
  547. LnkLstPtr import_copy_of(const LnkLstPtr& original);
  548. LnkSetPtr import_copy_of(const LnkSetPtr& original);
  549. // handover of the heavier Query and TableView
  550. std::unique_ptr<Query> import_copy_of(Query&, PayloadPolicy);
  551. std::unique_ptr<TableView> import_copy_of(TableView&, PayloadPolicy);
  552. std::unique_ptr<ConstTableView> import_copy_of(ConstTableView&, PayloadPolicy);
  553. /// Get the current transaction type
  554. DB::TransactStage get_transact_stage() const noexcept;
  555. /// Get a version id which may be used to request a different transaction locked to specific version.
  556. VersionID get_version_of_current_transaction();
  557. void upgrade_file_format(int target_file_format_version);
  558. private:
  559. DBRef get_db() const
  560. {
  561. return db;
  562. }
  563. Replication* const* get_repl() const final
  564. {
  565. return db->get_repl();
  566. }
  567. template <class O>
  568. bool internal_advance_read(O* observer, VersionID target_version, _impl::History&, bool);
  569. void set_transact_stage(DB::TransactStage stage) noexcept;
  570. void do_end_read() noexcept;
  571. void commit_and_continue_writing();
  572. void initialize_replication();
  573. DBRef db;
  574. mutable std::unique_ptr<_impl::History> m_history_read;
  575. mutable _impl::History* m_history = nullptr;
  576. DB::ReadLockInfo m_read_lock;
  577. DB::TransactStage m_transact_stage = DB::transact_Ready;
  578. friend class DB;
  579. friend class DisableReplication;
  580. };
  581. class DisableReplication {
  582. public:
  583. DisableReplication(Transaction& t)
  584. : m_tr(t)
  585. , m_owner(t.get_db())
  586. , m_repl(m_owner->get_replication())
  587. , m_version(t.get_version())
  588. {
  589. m_owner->set_replication(nullptr);
  590. t.get_version();
  591. t.m_history = nullptr;
  592. }
  593. ~DisableReplication()
  594. {
  595. m_owner->set_replication(m_repl);
  596. if (m_version != m_tr.get_version())
  597. m_tr.initialize_replication();
  598. }
  599. private:
  600. Transaction& m_tr;
  601. DBRef m_owner;
  602. Replication* m_repl;
  603. DB::version_type m_version;
  604. };
  605. /*
  606. * classes providing backward Compatibility with the older
  607. * ReadTransaction and WriteTransaction types.
  608. */
  609. class ReadTransaction {
  610. public:
  611. ReadTransaction(DBRef sg)
  612. : trans(sg->start_read())
  613. {
  614. }
  615. ~ReadTransaction() noexcept {}
  616. operator Transaction&()
  617. {
  618. return *trans;
  619. }
  620. bool has_table(StringData name) const noexcept
  621. {
  622. return trans->has_table(name);
  623. }
  624. ConstTableRef get_table(TableKey key) const
  625. {
  626. return trans->get_table(key); // Throws
  627. }
  628. ConstTableRef get_table(StringData name) const
  629. {
  630. return trans->get_table(name); // Throws
  631. }
  632. const Group& get_group() const noexcept
  633. {
  634. return *trans.get();
  635. }
  636. /// Get the version of the snapshot to which this read transaction is bound.
  637. DB::version_type get_version() const noexcept
  638. {
  639. return trans->get_version();
  640. }
  641. private:
  642. TransactionRef trans;
  643. };
  644. class WriteTransaction {
  645. public:
  646. WriteTransaction(DBRef sg)
  647. : trans(sg->start_write())
  648. {
  649. }
  650. ~WriteTransaction() noexcept {}
  651. operator Transaction&()
  652. {
  653. return *trans;
  654. }
  655. bool has_table(StringData name) const noexcept
  656. {
  657. return trans->has_table(name);
  658. }
  659. TableRef get_table(TableKey key) const
  660. {
  661. return trans->get_table(key); // Throws
  662. }
  663. TableRef get_table(StringData name) const
  664. {
  665. return trans->get_table(name); // Throws
  666. }
  667. TableRef add_table(StringData name) const
  668. {
  669. return trans->add_table(name); // Throws
  670. }
  671. TableRef add_embedded_table(StringData name) const
  672. {
  673. return trans->add_embedded_table(name); // Throws
  674. }
  675. TableRef get_or_add_table(StringData name, bool* was_added = nullptr) const
  676. {
  677. return trans->get_or_add_table(name, was_added); // Throws
  678. }
  679. Group& get_group() const noexcept
  680. {
  681. return *trans.get();
  682. }
  683. /// Get the version of the snapshot on which this write transaction is
  684. /// based.
  685. DB::version_type get_version() const noexcept
  686. {
  687. return trans->get_version();
  688. }
  689. DB::version_type commit()
  690. {
  691. return trans->commit();
  692. }
  693. void rollback() noexcept
  694. {
  695. trans->rollback();
  696. }
  697. private:
  698. TransactionRef trans;
  699. };
  700. // Implementation:
  701. struct DB::BadVersion : std::exception {
  702. };
  703. inline bool DB::is_attached() const noexcept
  704. {
  705. return bool(m_fake_read_lock_if_immutable) || m_file_map.is_attached();
  706. }
  707. inline DB::TransactStage Transaction::get_transact_stage() const noexcept
  708. {
  709. return m_transact_stage;
  710. }
  711. class DB::ReadLockGuard {
  712. public:
  713. ReadLockGuard(DB& shared_group, ReadLockInfo& read_lock) noexcept
  714. : m_shared_group(shared_group)
  715. , m_read_lock(&read_lock)
  716. {
  717. }
  718. ~ReadLockGuard() noexcept
  719. {
  720. if (m_read_lock)
  721. m_shared_group.release_read_lock(*m_read_lock);
  722. }
  723. void release() noexcept
  724. {
  725. m_read_lock = 0;
  726. }
  727. private:
  728. DB& m_shared_group;
  729. ReadLockInfo* m_read_lock;
  730. };
  731. template <class O>
  732. inline void Transaction::advance_read(O* observer, VersionID version_id)
  733. {
  734. if (m_transact_stage != DB::transact_Reading)
  735. throw LogicError(LogicError::wrong_transact_state);
  736. // It is an error if the new version precedes the currently bound one.
  737. if (version_id.version < m_read_lock.m_version)
  738. throw LogicError(LogicError::bad_version);
  739. auto hist = get_history(); // Throws
  740. if (!hist)
  741. throw LogicError(LogicError::no_history);
  742. internal_advance_read(observer, version_id, *hist, false); // Throws
  743. }
  744. template <class O>
  745. inline bool Transaction::promote_to_write(O* observer, bool nonblocking)
  746. {
  747. if (m_transact_stage != DB::transact_Reading)
  748. throw LogicError(LogicError::wrong_transact_state);
  749. if (nonblocking) {
  750. bool succes = db->do_try_begin_write();
  751. if (!succes) {
  752. return false;
  753. }
  754. }
  755. else {
  756. db->do_begin_write(); // Throws
  757. }
  758. try {
  759. Replication* repl = db->get_replication();
  760. if (!repl)
  761. throw LogicError(LogicError::no_history);
  762. VersionID version = VersionID(); // Latest
  763. m_history = repl->_get_history_write();
  764. bool history_updated = internal_advance_read(observer, version, *m_history, true); // Throws
  765. REALM_ASSERT(repl); // Presence of `repl` follows from the presence of `hist`
  766. DB::version_type current_version = m_read_lock.m_version;
  767. m_alloc.init_mapping_management(current_version);
  768. repl->initiate_transact(*this, current_version, history_updated); // Throws
  769. // If the group has no top array (top_ref == 0), create a new node
  770. // structure for an empty group now, to be ready for modifications. See
  771. // also Group::attach_shared().
  772. if (!m_top.is_attached())
  773. create_empty_group(); // Throws
  774. }
  775. catch (...) {
  776. db->do_end_write();
  777. m_history = nullptr;
  778. throw;
  779. }
  780. set_transact_stage(DB::transact_Writing);
  781. return true;
  782. }
  783. template <class O>
  784. inline void Transaction::rollback_and_continue_as_read(O* observer)
  785. {
  786. if (m_transact_stage != DB::transact_Writing)
  787. throw LogicError(LogicError::wrong_transact_state);
  788. Replication* repl = db->get_replication();
  789. if (!repl)
  790. throw LogicError(LogicError::no_history);
  791. BinaryData uncommitted_changes = repl->get_uncommitted_changes();
  792. // Possible optimization: We are currently creating two transaction log parsers, one here,
  793. // and one in advance_transact(). That is wasteful as the parser creation is
  794. // expensive.
  795. _impl::SimpleInputStream in(uncommitted_changes.data(), uncommitted_changes.size());
  796. _impl::TransactLogParser parser; // Throws
  797. _impl::TransactReverser reverser;
  798. parser.parse(in, reverser); // Throws
  799. if (observer && uncommitted_changes.size()) {
  800. _impl::ReversedNoCopyInputStream reversed_in(reverser);
  801. parser.parse(reversed_in, *observer); // Throws
  802. observer->parse_complete(); // Throws
  803. }
  804. // Mark all managed space (beyond the attached file) as free.
  805. db->reset_free_space_tracking(); // Throws
  806. ref_type top_ref = m_read_lock.m_top_ref;
  807. size_t file_size = m_read_lock.m_file_size;
  808. _impl::ReversedNoCopyInputStream reversed_in(reverser);
  809. m_alloc.update_reader_view(file_size); // Throws
  810. update_allocator_wrappers(false);
  811. advance_transact(top_ref, reversed_in, false); // Throws
  812. db->do_end_write();
  813. repl->abort_transact();
  814. m_history = nullptr;
  815. set_transact_stage(DB::transact_Reading);
  816. }
  817. template <class O>
  818. inline bool Transaction::internal_advance_read(O* observer, VersionID version_id, _impl::History& hist, bool writable)
  819. {
  820. DB::ReadLockInfo new_read_lock;
  821. db->grab_read_lock(new_read_lock, version_id); // Throws
  822. REALM_ASSERT(new_read_lock.m_version >= m_read_lock.m_version);
  823. if (new_read_lock.m_version == m_read_lock.m_version) {
  824. db->release_read_lock(new_read_lock);
  825. // _impl::History::update_early_from_top_ref() was not called
  826. // update allocator wrappers merely to update write protection
  827. update_allocator_wrappers(writable);
  828. return false;
  829. }
  830. DB::version_type old_version = m_read_lock.m_version;
  831. DB::ReadLockGuard g(*db, new_read_lock);
  832. DB::version_type new_version = new_read_lock.m_version;
  833. size_t new_file_size = new_read_lock.m_file_size;
  834. ref_type new_top_ref = new_read_lock.m_top_ref;
  835. // Synchronize readers view of the file
  836. SlabAlloc& alloc = m_alloc;
  837. alloc.update_reader_view(new_file_size);
  838. update_allocator_wrappers(writable);
  839. using gf = _impl::GroupFriend;
  840. ref_type hist_ref = gf::get_history_ref(alloc, new_top_ref);
  841. hist.update_from_ref_and_version(hist_ref, new_version);
  842. if (observer) {
  843. // This has to happen in the context of the originally bound snapshot
  844. // and while the read transaction is still in a fully functional state.
  845. _impl::TransactLogParser parser;
  846. _impl::ChangesetInputStream in(hist, old_version, new_version);
  847. parser.parse(in, *observer); // Throws
  848. observer->parse_complete(); // Throws
  849. }
  850. // The old read lock must be retained for as long as the change history is
  851. // accessed (until Group::advance_transact() returns). This ensures that the
  852. // oldest needed changeset remains in the history, even when the history is
  853. // implemented as a separate unversioned entity outside the Realm (i.e., the
  854. // old implementation and ShortCircuitHistory in
  855. // test_lang_Bind_helper.cpp). On the other hand, if it had been the case,
  856. // that the history was always implemented as a versioned entity, that was
  857. // part of the Realm state, then it would not have been necessary to retain
  858. // the old read lock beyond this point.
  859. _impl::ChangesetInputStream in(hist, old_version, new_version);
  860. advance_transact(new_top_ref, in, writable); // Throws
  861. g.release();
  862. db->release_read_lock(m_read_lock);
  863. m_read_lock = new_read_lock;
  864. return true; // _impl::History::update_early_from_top_ref() was called
  865. }
  866. inline int DB::get_file_format_version() const noexcept
  867. {
  868. return m_file_format_version;
  869. }
  870. } // namespace realm
  871. #endif // REALM_GROUP_SHARED_HPP