blob: 832ef6a091b5fd94dab04c81f60f0014e547f1ac [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 <string>
#include <array>
#include "lib/fidl/cpp/binding.h"
#include "lib/fidl/cpp/interface_request.h"
#include "lib/fxl/macros.h"
#include <fuchsia/cpp/ledger.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 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 : ledger::PageWatcher {
public:
// Takes a context name as a label for the error messages it logs. The ledger
// client reference is to receive conflicts from the ledger.
explicit PageClient(std::string context,
LedgerClient* ledger_client,
LedgerPageId page_id,
std::string prefix = "");
~PageClient() override;
// Returns the current page snapshot. It is returned as a shared_ptr, so that
// it can be used in an asynchronous operation. In that case, the page
// snapshot might be replaced by a new one from an incoming page watcher
// notification, but the client needs to hold onto the previous one until its
// operation completes.
//
// CAVEAT. To use this snapshot does not make sense for most clients (in fact
// it's no longer used in the modular code base). If the client implements
// page write operations and page read operations, an invariant normally
// maintained is that a read operation returns a value from *after* a
// preceding write (which might be a different value than the one written when
// there were merges from network sync), but never from *before* the preceding
// write. This invariant is maintained when the read operation uses a fresh
// snapshot, but not when the read operation uses the latest watcher snapshot
// (because the watcher notification from the write might not yet have arrived
// when the read is executed). Since all modular page clients have read and
// write operations where this invariant is desired, they all use fresh page
// snapshots.
std::shared_ptr<ledger::PageSnapshotPtr> page_snapshot() {
return page_snapshot_;
}
const ledger::PageId& page_id() const { return page_id_; }
const std::string& prefix() const { return prefix_; }
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 {
fidl::VectorPtr<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{};
};
private:
// Derived classes implement these methods as needed. The default
// implementation does nothing.
virtual void OnPageChange(const std::string& key, const std::string& value);
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);
// Replaces the previous page snapshot with a newly requested one.
fidl::InterfaceRequest<ledger::PageSnapshot> NewRequest();
// Possibly replaces the previous page snapshot with a new one
// requested through the result callback of a PageWatcher, depending
// on the continuation code of the watcher notification.
fidl::InterfaceRequest<ledger::PageSnapshot> Update(
ledger::ResultState result_state);
// |PageWatcher|
void OnChange(ledger::PageChange page,
ledger::ResultState result_state,
OnChangeCallback callback) override;
fidl::Binding<ledger::PageWatcher> binding_;
const std::string context_;
LedgerClient* const ledger_client_;
const ledger::PageId page_id_;
ledger::Page* const page_;
const std::string prefix_;
std::shared_ptr<ledger::PageSnapshotPtr> page_snapshot_;
FXL_DISALLOW_COPY_AND_ASSIGN(PageClient);
};
// Retrieves all entries from the given snapshot and calls the given callback
// with the final status.
void GetEntries(ledger::PageSnapshot* snapshot,
std::vector<ledger::Entry>* entries,
std::function<void(ledger::Status)> callback);
} // namespace modular
#endif // PERIDOT_LIB_LEDGER_CLIENT_PAGE_CLIENT_H_