blob: 81785aa9c49dffb4a38c326b4ba4287e625dc682 [file] [log] [blame]
// Copyright 2017 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 PERIDOT_LIB_LEDGER_CLIENT_PAGE_CLIENT_H_
#define PERIDOT_LIB_LEDGER_CLIENT_PAGE_CLIENT_H_
#include <array>
#include <string>
#include <fuchsia/ledger/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/fxl/macros.h>
#include "peridot/lib/ledger_client/types.h"
namespace modular {
class LedgerClient;
// A helper class that holds on to a page snapshot through a shared
// pointer, hands out shared pointers to it, can replace the snapshot
// by a new one, and registers a connection error handler on it. It
// essentially wraps fuchsia::ledger::PageSnapshot.
//
// This is used by classes that hold onto a PageSnapshot and update it
// in return calls from PageWatcher notifications, and use Operation
// containers to access the snapshot.
//
// Every time we receive an OnChange notification, we update the page
// snapshot so we see the current state. Just in case, we also install
// a connection error handler on the snapshot connection, so we can
// log when the connection unexpectedly closes, although we cannot do
// anything else about it.
//
// An instance of this class always holds the current snapshot of the
// page we read from, as obtained from the watcher on the page. It is
// held by a shared pointer, because the update may occur while
// Operation instances that read from it are still in progress, and
// they need to hold on to the same snapshot they started with, lest
// the methods called on that snapshot never return.
//
// The same behavior as with a shared_ptr could be accomplished with
// a duplicate PageSnapshotPtr for each Operation instance that needs
// one, but PageSnapshot doesn't have a duplicate method.
class PageClient : fuchsia::ledger::PageWatcher {
public:
// |context| is used as a string prefix on log messages. |ledger_client| is
// used to retrieve a handle to the page specified in |page_id|, and to
// listen for conflicts from the ledger. If |prefix| is provided, the
// resulting page snapshot and change notifications are limited to only keys
// with that prefix. However, OnPageChange()'s |key| will include the full
// key, including the prefix.
//
// |ledger_client| must outlive *this.
PageClient(std::string context, LedgerClient* ledger_client,
LedgerPageId page_id, std::string prefix = "");
~PageClient() override;
// Returns a snapshot of the Ledger page under |prefix| at the most recent
// timepoint.
//
// If |on_error| is provided, it will be called if there was a Ledger error
// trying to get the snapshot.
//
// NOTE: There is no guaranteed timing for writes made to the returned
// PageSnapshot and notifications of changes through OnPageChange(). The
// ordering is guaranteed to be the same, ignoring changes to the writes
// caused by conflict resolution which can cause some writes to disappear.
fuchsia::ledger::PageSnapshotPtr NewSnapshot(
std::function<void()> on_error = nullptr);
const fuchsia::ledger::PageId& page_id() const { return page_id_; }
const std::string& prefix() const { return prefix_; }
fuchsia::ledger::Page* page() { return page_; }
// Computed by implementations of OnPageConflict() in derived classes.
enum ConflictResolution { LEFT, RIGHT, MERGE };
// The argument to OnPageConflict(). It's mutated in place so it's easier to
// extend without having to alter clients.
struct Conflict {
std::vector<uint8_t> key;
bool has_left{};
std::string left;
bool left_is_deleted{};
bool has_right{};
std::string right;
bool right_is_deleted{};
ConflictResolution resolution{LEFT};
std::string merged;
bool merged_is_deleted{};
};
protected:
// Derived classes implement this method as needed. The default implementation
// copies the VMO to a string and forwards to |OnPageChange(const
// std::string&, const std::string&)|.
virtual void OnPageChange(const std::string& key,
fuchsia::mem::BufferPtr value);
private:
// Derived classes implement this method as needed. The default implementation
// does nothing. This method is only called if forwarded from
// |OnPageChange(const std::string&, fuchsia::mem::BufferPtr)|.
virtual void OnPageChange(const std::string& key, const std::string& value);
// Derived classes implement this method as needed. The default implementation
// does nothing.
virtual void OnPageDelete(const std::string& key);
// Derived classes implement this method as needed. The default implementation
// selects left and logs an INFO about the unresolved conflict.
//
// For now, only per-key conflict resolution is supported by page client. If
// we need more coherency for conflict resolution, this can be changed.
//
// For now, conflict resolution is synchronous. Can be changed too, for
// example to go on an OperationQueue to wait for ongoing changes to reconcile
// with.
//
// If ConflictResolution is MERGE, the result is returned in merged*. It is
// possible that the merge of two undeleted values is to the delete the key.
//
// This is invoked from the conflict resolver in LedgerClient.
friend class LedgerClient;
virtual void OnPageConflict(Conflict* conflict);
// |PageWatcher|
void OnChange(fuchsia::ledger::PageChange page,
fuchsia::ledger::ResultState result_state,
OnChangeCallback callback) override;
fidl::Binding<fuchsia::ledger::PageWatcher> binding_;
const std::string context_;
LedgerClient* const ledger_client_;
const fuchsia::ledger::PageId page_id_;
fuchsia::ledger::Page* const page_;
const std::string prefix_;
FXL_DISALLOW_COPY_AND_ASSIGN(PageClient);
};
// Retrieves all entries from the given snapshot and calls the given callback
// with the final status.
//
// The FIDL pointer backing |snapshot| must have the same life time as
// |entries|, so that callbacks are cancelled when |entries| are deleted before
// |callback| is invoked.
void GetEntries(fuchsia::ledger::PageSnapshot* snapshot,
std::vector<fuchsia::ledger::Entry>* entries,
std::function<void(fuchsia::ledger::Status)> done);
} // namespace modular
#endif // PERIDOT_LIB_LEDGER_CLIENT_PAGE_CLIENT_H_