blob: fe8dba77f8aa779a24a87310919b037bf1df3169 [file] [log] [blame]
// 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_CLOUD_SYNC_IMPL_BATCH_UPLOAD_H_
#define SRC_LEDGER_BIN_CLOUD_SYNC_IMPL_BATCH_UPLOAD_H_
#include <fuchsia/ledger/cloud/cpp/fidl.h>
#include <lib/fit/function.h>
#include <functional>
#include <memory>
#include <vector>
#include "src/ledger/bin/encryption/public/encryption_service.h"
#include "src/ledger/bin/fidl/include/types.h"
#include "src/ledger/bin/storage/public/commit.h"
#include "src/ledger/bin/storage/public/page_storage.h"
#include "src/ledger/lib/coroutine/coroutine.h"
#include "src/ledger/lib/coroutine/coroutine_manager.h"
#include "src/ledger/lib/memory/weak_ptr.h"
namespace cloud_sync {
// Uploads a batch of commits along with unsynced storage objects and marks
// the uploaded artifacts as synced.
//
// Contract: The class doesn't reason about objects referenced by each commit,
// and instead uploads each unsynced object present in storage at the moment of
// calling Start(). Unsynced objects are marked as synced as they are uploaded.
// The commits in the batch are uploaded in one network request once all objects
// are uploaded.
//
// Usage: call Start() to kick off the upload. |on_done| is called after the
// upload is successfully completed. |on_error| will be called at most once
// after each error. Each time after |on_error| is called the client can
// call Retry() once to retry the upload.
// TODO(ppi): rather than DCHECK on storage errors, take separate callbacks for
// network and disk errors and let PageSync decide on how to handle each.
//
// Lifetime: if BatchUpload is deleted between Start() and |on_done| being
// called, it has to be deleted along with |storage| and |cloud_provider|, which
// otherwise can retain callbacks for pending uploads. This isn't a problem as
// long as the lifetime of page storage and page sync is managed together.
class BatchUpload {
public:
// In case of error in BatchUpload, ErrorType defines whether the error that
// occurred is temporary (from cloud or auth provider), or permanent (from
// storage or from encryption).
enum class ErrorType {
PERMANENT,
TEMPORARY,
};
BatchUpload(coroutine::CoroutineService* coroutine_service, storage::PageStorage* storage,
encryption::EncryptionService* encryption_service,
cloud_provider::PageCloudPtr* page_cloud,
std::vector<std::unique_ptr<const storage::Commit>> commits, fit::closure on_done,
fit::function<void(ErrorType)> on_error, unsigned int max_concurrent_uploads = 10);
BatchUpload(const BatchUpload&) = delete;
BatchUpload& operator=(const BatchUpload&) = delete;
~BatchUpload();
// Starts a new upload attempt. Results are reported through |on_done|
// and |on_error| passed in the constructor. Can be called only once.
void Start();
// Retries the attempt to upload the commit batch. Each time |on_error| is called with a temporary
// error, the client can retry by calling this method.
void Retry();
private:
// Status of an upload operation: successful, or the type of the error to return.
// This is ordered from best status to worst status.
enum class UploadStatus {
OK,
TEMPORARY_ERROR,
PERMANENT_ERROR,
};
// Converts an encryption status to an upload status.
static UploadStatus EncryptionStatusToUploadStatus(encryption::Status status);
// Converts a ledger status to an upload status.
static UploadStatus LedgerStatusToUploadStatus(ledger::Status status);
// Converts a cloud provider status to an upload status.
static UploadStatus CloudStatusToUploadStatus(cloud_provider::Status status);
// Converts a non-OK upload status to its error type.
static ErrorType UploadStatusToErrorType(UploadStatus status);
// Sets the upload status to |status| if it is worse than the current status.
void SetUploadStatus(UploadStatus status);
// Calls |on_error_| with the current error status.
void SignalError();
void StartObjectUpload();
// Reads, encrypts and uploads one object. Errors are signaled in |status_|. The caller is
// responsible for calling the |on_error_| callback when appropriate.
void SynchronousUploadObject(coroutine::CoroutineHandler* handler,
storage::ObjectIdentifier identifier);
// Filters already synced commits.
void FilterAndUploadCommits();
// Uploads the commits.
void UploadCommits();
// Re-enqueue the object for another upload attempt.
void EnqueueForRetry(storage::ObjectIdentifier object_identifier);
// Encodes a commit for sending to the cloud.
void EncodeCommit(const storage::Commit& commit,
fit::function<void(UploadStatus, cloud_provider::Commit)> callback);
// Encodes a diff for sending to the cloud.
void EncodeDiff(storage::CommitIdView commit_id, std::vector<storage::EntryChange> entries,
fit::function<void(UploadStatus, cloud_provider::Diff)> callback);
// Encodes a diff entry for sending to the cloud.
void EncodeEntry(storage::EntryChange entry,
fit::function<void(UploadStatus, cloud_provider::DiffEntry)> callback);
storage::PageStorage* const storage_;
encryption::EncryptionService* const encryption_service_;
cloud_provider::PageCloudPtr* const page_cloud_;
std::vector<std::unique_ptr<const storage::Commit>> commits_;
fit::closure on_done_;
fit::function<void(ErrorType)> on_error_;
// All remaining object ids to be uploaded along with this batch of commits.
std::vector<storage::ObjectIdentifier> remaining_object_identifiers_;
bool started_ = false;
// Stores the status of the upload. If multiple errors have been encountered, stores the worst
// error (permanent if any permanent error has been encountered, temporary otherwise).
//
// Transitions: this always goes from best to worst (OK -> TEMPORARY_ERROR -> PERMANENT_ERROR),
// except in |Retry| where the status can transition from |TEMPORARY_ERROR| to |OK|.
UploadStatus status_ = UploadStatus::OK;
coroutine::CoroutineManager coroutine_manager_;
// Must be the last member.
ledger::WeakPtrFactory<BatchUpload> weak_ptr_factory_;
};
} // namespace cloud_sync
#endif // SRC_LEDGER_BIN_CLOUD_SYNC_IMPL_BATCH_UPLOAD_H_