| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_LEDGER_BIN_STORAGE_IMPL_PAGE_DB_H_ |
| #define SRC_LEDGER_BIN_STORAGE_IMPL_PAGE_DB_H_ |
| |
| #include <lib/zx/time.h> |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "src/ledger/bin/storage/public/data_source.h" |
| #include "src/ledger/bin/storage/public/db.h" |
| #include "src/ledger/bin/storage/public/iterator.h" |
| #include "src/ledger/bin/storage/public/object.h" |
| #include "src/ledger/bin/storage/public/types.h" |
| #include "src/ledger/lib/coroutine/coroutine.h" |
| #include "third_party/abseil-cpp/absl/base/attributes.h" |
| #include "third_party/abseil-cpp/absl/strings/string_view.h" |
| |
| namespace storage { |
| |
| class PageStorageImpl; |
| |
| // Status of an object in the database. |
| enum class PageDbObjectStatus { |
| // The object is not in the database. |
| UNKNOWN, |
| // The object is in the database, but not in any commit. |
| TRANSIENT, |
| // The object is associated to a commit, but not yet synced. |
| LOCAL, |
| // The object is synced. |
| SYNCED, |
| }; |
| |
| // IMPORTANT NOTE for the correctness of garbage-collection: |
| // When adding or modifying methods to |PageDbMutator| and |PageDb|, use ObjectIdentifier for inputs |
| // to ensure concurrent calls to DeleteObject for the same object are aborted. This is necessary |
| // even if, at the storage level, the method only needs to read or write an ObjectDigest: tracking |
| // of live references occurs at the ObjectIdentifier level. The only exception is DeleteObject |
| // itself, to avoid deletion aborting itself. |
| // See the implementation comment on |DeleteObject| in page_db_batch_impl.cc for more details. |
| |
| // |PageDbMutator| provides all update (insertion and deletion) operations |
| // over |PageDb|. |
| class PageDbMutator { |
| public: |
| PageDbMutator() = default; |
| PageDbMutator(const PageDbMutator&) = delete; |
| PageDbMutator& operator=(const PageDbMutator&) = delete; |
| virtual ~PageDbMutator() = default; |
| |
| // Heads. |
| // Adds the given |head| in the set of commit heads. |
| ABSL_MUST_USE_RESULT virtual Status AddHead(coroutine::CoroutineHandler* handler, |
| CommitIdView head, zx::time_utc timestamp) = 0; |
| |
| // Removes the given |head| from the head commits. |
| ABSL_MUST_USE_RESULT virtual Status RemoveHead(coroutine::CoroutineHandler* handler, |
| CommitIdView head) = 0; |
| |
| // Merges. |
| // Adds the commit with id |merge_commit_id| in the set of merges of commits |
| // with ids |parent1_id| and |parent2_id|. |
| ABSL_MUST_USE_RESULT virtual Status AddMerge(coroutine::CoroutineHandler* handler, |
| CommitIdView parent1_id, CommitIdView parent2_id, |
| CommitIdView merge_commit_id) = 0; |
| |
| ABSL_MUST_USE_RESULT virtual Status DeleteMerge(coroutine::CoroutineHandler* handler, |
| CommitIdView parent1_id, CommitIdView parent2_id, |
| CommitIdView commit_id) = 0; |
| |
| // Commits. |
| // Adds the given |commit|, referencing |root_node|, in the database. |
| ABSL_MUST_USE_RESULT virtual Status AddCommitStorageBytes(coroutine::CoroutineHandler* handler, |
| const CommitId& commit_id, |
| absl::string_view remote_commit_id, |
| const ObjectIdentifier& root_node, |
| absl::string_view storage_bytes) = 0; |
| |
| // Deletes the commit with given |commit_id|, referencing |root_node|, from the database. |
| ABSL_MUST_USE_RESULT virtual Status DeleteCommit(coroutine::CoroutineHandler* handler, |
| CommitIdView commit_id, |
| absl::string_view remote_commit_id, |
| const ObjectIdentifier& root_node) = 0; |
| |
| // Object data. |
| // Writes the content of the given object, and reference information from this |
| // object to its children. |
| ABSL_MUST_USE_RESULT virtual Status WriteObject( |
| coroutine::CoroutineHandler* handler, const Piece& piece, PageDbObjectStatus object_status, |
| const ObjectReferencesAndPriority& references) = 0; |
| |
| // Deletes the object with the given |object_digest|, and reference information from this |
| // object to its children. Aborts and returns an INTERNAL_ERROR if the object is referenced either |
| // from other on-disk objects, or from in-memory object identifiers. |
| // In the |PageDb::Batch| implementation of this method, if the object becomes referenced between |
| // this method successfully returning and |Execute| being called, |Execute| will return an error |
| // instead (but other intermediate method calls on the batch will be unaffected). |
| ABSL_MUST_USE_RESULT virtual Status DeleteObject( |
| coroutine::CoroutineHandler* handler, const ObjectDigest& object_digest, |
| const ObjectReferencesAndPriority& references) = 0; |
| |
| // Object sync metadata. |
| // Sets the status of the object with the given id. |
| ABSL_MUST_USE_RESULT virtual Status SetObjectStatus(coroutine::CoroutineHandler* handler, |
| const ObjectIdentifier& object_identifier, |
| PageDbObjectStatus object_status) = 0; |
| |
| // Commit sync metadata. |
| // Marks the given |commit_id| as synced. |
| ABSL_MUST_USE_RESULT virtual Status MarkCommitIdSynced(coroutine::CoroutineHandler* handler, |
| const CommitId& commit_id) = 0; |
| |
| // Marks the given |commit_id| as unsynced. |
| ABSL_MUST_USE_RESULT virtual Status MarkCommitIdUnsynced(coroutine::CoroutineHandler* handler, |
| const CommitId& commit_id, |
| uint64_t generation) = 0; |
| |
| // Sets the opaque sync metadata associated with this page for the given key. |
| ABSL_MUST_USE_RESULT virtual Status SetSyncMetadata(coroutine::CoroutineHandler* handler, |
| absl::string_view key, |
| absl::string_view value) = 0; |
| |
| // Updates the online state of the page. |
| ABSL_MUST_USE_RESULT virtual Status MarkPageOnline(coroutine::CoroutineHandler* handler) = 0; |
| |
| // Clock management: |
| // Sets a unique ID for this device on this page. |
| ABSL_MUST_USE_RESULT virtual Status SetDeviceId(coroutine::CoroutineHandler* handler, |
| const clocks::DeviceId& device_id) = 0; |
| |
| // Sets the clock for a device. This method entirely replaces any existing clock, including |
| // removing device entries missing from |clock| but present locally. |
| ABSL_MUST_USE_RESULT virtual Status SetClock(coroutine::CoroutineHandler* handler, |
| const Clock& clock) = 0; |
| }; |
| |
| // |PageDb| manages all Ledger related data that are locally stored. This |
| // includes commit, value and tree node objects, information on head commits, as |
| // well as metadata on which objects and commits are not yet synchronized to the |
| // cloud. |
| class PageDb : public PageDbMutator { |
| public: |
| // A |Batch| can be used to execute a number of updates in |PageDb| atomically. |
| // No further operations in a batch are supported after a failed call to any method. |
| // |
| // ObjectIdentifiers passed to batch methods are not automatically kept alive: it is the caller's |
| // responsibility to keep the objects alive until |Execute| has completed, and potentially longer |
| // if they cannot ensure that they already have on-disk references at that point. |
| class Batch : public PageDbMutator { |
| public: |
| Batch() = default; |
| Batch(const Batch&) = delete; |
| Batch& operator=(const Batch&) = delete; |
| ~Batch() override = default; |
| |
| // Executes this batch. No further operations in this batch are supported |
| // after a successful execution. |
| ABSL_MUST_USE_RESULT virtual Status Execute(coroutine::CoroutineHandler* handler) = 0; |
| }; |
| |
| PageDb() = default; |
| PageDb(const PageDb&) = delete; |
| PageDb& operator=(const PageDb&) = delete; |
| ~PageDb() override = default; |
| |
| // Starts a new batch. The batch will be written when Execute is called on the |
| // returned object. The PageDb object must outlive the batch object. If the |
| // coroutine is interrupted, |INTERRUPTED| status is returned. |
| ABSL_MUST_USE_RESULT virtual Status StartBatch(coroutine::CoroutineHandler* handler, |
| std::unique_ptr<Batch>* batch) = 0; |
| |
| // Heads. |
| // Finds all head commits and replaces the contents of |heads| with their ids. |
| // Returns |OK| on success or |IO_ERROR| in case of an error reading the |
| // values. It is not an error if no heads are found. The resulting |heads| are |
| // ordered by the timestamp given at their insertion and if identical, by |
| // their id. |
| ABSL_MUST_USE_RESULT virtual Status GetHeads( |
| coroutine::CoroutineHandler* handler, |
| std::vector<std::pair<zx::time_utc, CommitId>>* heads) = 0; |
| |
| // Merges. |
| // Finds all merges of the commits with ids |parent1_id| and |parent2_id|, and |
| // returns their ids. |
| ABSL_MUST_USE_RESULT virtual Status GetMerges(coroutine::CoroutineHandler* handler, |
| CommitIdView parent1_id, CommitIdView parent2_id, |
| std::vector<CommitId>* heads) = 0; |
| |
| // Commits. |
| // Finds the commit with the given |commit_id| and stores its represenation in |
| // storage bytes in the |storage_bytes| string. |
| ABSL_MUST_USE_RESULT virtual Status GetCommitStorageBytes(coroutine::CoroutineHandler* handler, |
| CommitIdView commit_id, |
| std::string* storage_bytes) = 0; |
| |
| // Piece data. |
| // Reads the content of the given piece. |
| ABSL_MUST_USE_RESULT virtual Status ReadObject(coroutine::CoroutineHandler* handler, |
| const ObjectIdentifier& object_identifier, |
| std::unique_ptr<const Piece>* piece) = 0; |
| |
| // Checks whether the object with the given |object_digest| is stored in the |
| // database. Returns |OK| if the objet was found, or |INTERNAL_NOT_FOUND| if |
| // not. |
| ABSL_MUST_USE_RESULT virtual Status HasObject(coroutine::CoroutineHandler* handler, |
| const ObjectIdentifier& object_identifier) = 0; |
| |
| // Returns the status of the object with the given id. |
| ABSL_MUST_USE_RESULT virtual Status GetObjectStatus(coroutine::CoroutineHandler* handler, |
| const ObjectIdentifier& object_identifier, |
| PageDbObjectStatus* object_status) = 0; |
| |
| // Returns all object status keys in the underlying |Db| for a given |object_digest|, mapped to |
| // the status they encode. |
| ABSL_MUST_USE_RESULT virtual Status GetObjectStatusKeys( |
| coroutine::CoroutineHandler* handler, const ObjectDigest& object_digest, |
| std::map<std::string, PageDbObjectStatus>* keys) = 0; |
| |
| // Returns inbound object references towards the object with the given id. |
| // WARNING: this function is reversing the usual semantics of |
| // |ObjectReferencesAndPriority|. |references| contains |source| identifiers |
| // such that there are references from |source| to |object_identifier|. |
| ABSL_MUST_USE_RESULT virtual Status GetInboundObjectReferences( |
| coroutine::CoroutineHandler* handler, const ObjectIdentifier& object_identifier, |
| ObjectReferencesAndPriority* references) = 0; |
| |
| // Returns inbound commit references towards the object with the given id. |
| ABSL_MUST_USE_RESULT virtual Status GetInboundCommitReferences( |
| coroutine::CoroutineHandler* handler, const ObjectIdentifier& object_identifier, |
| std::vector<CommitId>* references) = 0; |
| |
| // Checks whether this object is deletable. Returns |Status::CANCELED| if the object cannot be |
| // deleted, |Status::OK| if it can be deleted. Callers that intend to perform the deletion based |
| // on this information must track the deletion in the object identifier factory to ensure no new |
| // references are created. Returns the list of keys storing the sync status of this object in |
| // |object_status_keys|. |
| ABSL_MUST_USE_RESULT virtual Status EnsureObjectDeletable( |
| coroutine::CoroutineHandler* handler, const ObjectDigest& object_digest, |
| std::vector<std::string>* object_status_keys) = 0; |
| |
| // Commit sync metadata. |
| // Finds the set of unsynced commits and replaces the contents of |commit_ids| |
| // with their ids. The result is ordered by the timestamps given when calling |
| // |MarkCommitIdUnsynced|. |
| ABSL_MUST_USE_RESULT virtual Status GetUnsyncedCommitIds(coroutine::CoroutineHandler* handler, |
| std::vector<CommitId>* commit_ids) = 0; |
| |
| // Checks if the commit with the given |commit_id| is synced. |
| ABSL_MUST_USE_RESULT virtual Status IsCommitSynced(coroutine::CoroutineHandler* handler, |
| const CommitId& commit_id, |
| bool* is_synced) = 0; |
| |
| // Object sync metadata. |
| // Finds the set of unsynced pieces and replaces the contents of |
| // |object_identifiers| with their identifiers. |
| ABSL_MUST_USE_RESULT virtual Status GetUnsyncedPieces( |
| coroutine::CoroutineHandler* handler, std::vector<ObjectIdentifier>* object_identifiers) = 0; |
| |
| // Sync metadata. |
| // Retrieves the opaque sync metadata associated with this page for the given |
| // key. |
| ABSL_MUST_USE_RESULT virtual Status GetSyncMetadata(coroutine::CoroutineHandler* handler, |
| absl::string_view key, |
| std::string* value) = 0; |
| |
| // Returns whether the page is online, i.e. has been synced to the cloud or a |
| // peer at least once from this device. By default, the state of a page is |
| // offline. Once the state is set to online, it cannot be unset. |
| ABSL_MUST_USE_RESULT virtual Status IsPageOnline(coroutine::CoroutineHandler* handler, |
| bool* page_is_online) = 0; |
| |
| // Clock management: |
| // Gets the unique ID for this device on this page. |
| ABSL_MUST_USE_RESULT virtual Status GetDeviceId(coroutine::CoroutineHandler* handler, |
| clocks::DeviceId* device_id) = 0; |
| |
| // Gets the full vector clock for this page as currently stored. |
| ABSL_MUST_USE_RESULT virtual Status GetClock(coroutine::CoroutineHandler* handler, |
| Clock* clock) = 0; |
| |
| ABSL_MUST_USE_RESULT virtual Status GetCommitIdFromRemoteId(coroutine::CoroutineHandler* handler, |
| absl::string_view remote_id, |
| CommitId* commit_id) = 0; |
| }; |
| |
| } // namespace storage |
| |
| #endif // SRC_LEDGER_BIN_STORAGE_IMPL_PAGE_DB_H_ |