blob: 59ffd84236d0c3388cbe9cee97d811c930653d9c [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef COBALT_ANALYZER_STORE_REPORT_STORE_H_
#define COBALT_ANALYZER_STORE_REPORT_STORE_H_
#include <memory>
#include <string>
#include <vector>
#include "analyzer/report_master/report_internal.pb.h"
#include "analyzer/report_master/report_master.pb.h"
#include "analyzer/store/data_store.h"
#include "util/clock.h"
namespace cobalt {
namespace analyzer {
namespace store {
// A ReportStore is used for storing and retrieving Cobalt reports. A
// report is the final output of the Cobalt pipeline--the result of the privacy-
// preserving analysis.
class ReportStore {
public:
// Constructs a ReportStore that wraps an underlying data store.
explicit ReportStore(std::shared_ptr<DataStore> store);
// Generates a new ReportId and writes information into the ReportStore to
// indicate that the report with that ID is in the IN_PROGRESS state. This
// method should be invoked prior to starting to add new rows to a report
// via the AddReportRow() method. The report being started is associated
// with a ReportConfig and it may be either the primary report specified
// by the ReportConfig or it may be an auxilliary report being automatically
// generated by the system in support of the primary report. The group of
// reports--primary and auxilliary--associated with a ReportConfig are known
// as a report dependency group. This method should be used on the *first*
// report in a dependency group. The other reports in the dependency group are
// created in the WAITING_TO_START state via the method CreateDependentReport
// and later started via the method StartDependentReport.
//
// |first_day_index| and |last_day_index| specify the range of day indices
// for which Observations will be analyzed for this report.
//
// |one_off| indicates whether this report is being explicitly requested
// as opposed to being generated by a regular schedule.
//
// |export_name| specifies the location to where this report will be exported.
// See the comments on the |export_name| field of ReportMetadataLite.
//
// |in_store| specifies whether or not the rows of this report will be
// stored in the ReportStore. See the comments on the |in_store| field of
// ReportMetadataLite. Note that if |report_type| is RAW_DUMP then this
// value must be false or kInvalidArguments, will be returned.
//
// |report_type| indicates which type of report is being started. The
// associated ReportConfig already specifies a ReportType. This |report_type|
// should be the same as that ReportType if the report being started is the
// primary report associated with the ReportConfig. But in case the report
// being started is an auxiallary report being automatically generated by the
// system, this |report_type| may be different from the primary ReportType.
// For example if the primary ReportType is JOINT the report being started
// may be one of the automatically generated one-way marginal reports in which
// case this |report_type| would be HISTOGRAM.
//
// |variable_indices| indicates which of the variables specified in the
// ReportConfig are being analyzed by the report being started. It consists
// of zero-based indices into the |variable| list in the ReportConfig.
//
// |report_id| is used for both input and output. On input all fields other
// than the |instance_id| and |creation_time_seconds| should be set. This
// method will set those fields thereby forming a new unique ReportId.
// Note that in |report_id| the |sequence_num| field should be set by the
// caller to be 0 (the default value) to indicate that the report is either
// not part of a dependency group of reports or else that it is the first
// report in its dependency group to be started.
Status StartNewReport(uint32_t first_day_index, uint32_t last_day_index,
bool one_off, const std::string& export_name,
bool in_store, ReportType report_type,
const std::vector<uint32_t>& variable_indices,
ReportId* report_id);
// Writes information into the ReportStore to indicate that a dependent report
// is in the WAITING_TO_START state. This method is in support of report
// dependency groups. StartNewReport() is used to start the first report
// in the group and this method is used to *create* but *not start* all
// subsequent reports. This method may be invoked at any time
// after StartNewReport() has been invoked for the first report. This
// is useful so that the later reports are registered in the ReportStore
// before the actual generation of the first report begins. The
// method StartDependentReport() should later be invoked when it is time
// to put the dependent report into the IN_PROGRESS_STATE which must be done
// before the generation of the dependent report begins.
//
// squence_number: The identifier of this report within its dependency group.
// The first report in the group has sequence_number 0 so this one should have
// a positive sequence number.
//
// |export_name| specifies the location to where this report will be exported.
// See the comments on the |export_name| field of ReportMetadataLite.
//
// |in_store| specifies whether or not the rows of this report will be
// stored in the ReportStore. See the comments on the |in_store| field of
// ReportMetadataLite. Note that if |report_type| is RAW_DUMP then this
// value must be false or kInvalidArguments will be returned.
//
// |report_type| indicates which type of report is being created. Note that
// different reports in a dependency group may have different ReportTypes.
//
// |variable_indices| indicates which of the variables specified in the
// ReportConfig associated with the dependency group are being analyzed by the
// report being created. It consists of zero-based indices into the
// |variable| list in the ReportConfig. Note that different reports in a
// dependency group may analyze different subsets of the variables.
//
// report_id: This is used for both input and output. On input this should be
// the complete ReportId for some earlier member of the dependency group.
// This id should have been previously returned from StartNewReport() for
// the first member of the group or from this method for a later member
// of the group. The |sequence_number| field of |report_id| will be
// updated to be equal to the given |sequence_number|, thereby forming a new
// ReportId which must not yet exist in the ReportStore. The
// |first_day_index|,
// |last_day_index|, and |one_off| fields of ReportMetadataLite will be
// copied from the existing report into the new report.
//
// Returns kOK on success, kNotFound if there is no existing report with
// the ReportId passed in, and kAlreadyExists if there is already a
// report with an ID of the new value of report_id obtained by setting the
// |sequence_number| field to the given |sequence_number|.
Status CreateDependentReport(uint32_t sequence_number,
const std::string& export_name, bool in_store,
ReportType report_type,
const std::vector<uint32_t>& variable_indices,
ReportId* report_id);
// Writes information into the ReportStore to indicate that the dependent
// report with the given |report_id| is transitioning from the
// WAITING_TO_START state into the IN_PROGRESS state. The report must
// already exist in the ReportStore and it must be in the
// WAITING_TO_START state.
//
// report_id: The ID of the dependent report to be started. This should have
// been returned from CreateDependentReport.
//
// Returns kOK on success, kNotFound if there is no existing report with
// the ReportId passed in, and kPreconditionFailed if the report is not
// in the WAITING_TO_START state.
Status StartDependentReport(const ReportId& report_id);
// Writes information into the ReportStore to indicate that the report with
// the given |report_id| has ended. If |success| is true then the report
// will now be in the COMPLETED_SUCCESSFULLY state, otherwise it will now be
// in the TERMINATED state. The |message| may hold additional information
// about the report such as an error message in the case |successful| is
// false. Returns kOK on success or kNotFound if there is no report with
// the given report_id.
Status EndReport(const ReportId& report_id, bool success,
std::string message);
// Adds ReportRows to the ReportStore for the report with the given id.
// This method should be invoked only after StartNewReport (or
// StartDependentReport as appropriate) has been invoked and the ReportId is
// therefore complete and the report is in the IN_PROGRESS state. This method
// is invoked repeatedly in order to output the results of an analysis. After
// all of the rows have been added with this method, the method EndReport()
// should be invoked.
//
// Note that the report with the given |report_id| is associated with a
// particular list of variables, namely those specified in the invocation
// of StartNewReport or CreateDependentReport. The values specified in
// |report_rows| must be for the appropriate list of variables. Returns
// kInvalidArguments if not.
//
// This method should only be invoked on a report for which |in_store| was
// set true when the metadata was created via StartNewReport or
// StartDependentReport. Otherwise INVALID_ARGUMENT is returned.
Status AddReportRows(const ReportId& report_id,
const std::vector<ReportRow>& report_rows);
// Gets the ReportMetadataLite for the report with the specified id.
Status GetMetadata(const ReportId& report_id,
ReportMetadataLite* metadata_out);
// Gets the Report with the specified id. If this method is invoked on a
// report for which |in_store| is not true then |report_out| will contain
// zero rows.
// TODO(rudominer) Consider not assuming a report fits in memory.
Status GetReport(const ReportId& report_id, ReportMetadataLite* metadata_out,
ReportRows* report_out);
// A ReportRecord is one of the results contained in the QueryReportsResponse
// returned from QueryReports(). It contains only meta-data. The report data
// is represented by ReportRows. This is a move-only type.
struct ReportRecord {
// Default constructor
ReportRecord() {}
// Move constructor.
ReportRecord(ReportRecord&& other) {
report_id.Swap(&other.report_id);
report_metadata.Swap(&other.report_metadata);
}
ReportId report_id;
ReportMetadataLite report_metadata;
};
// A QueryReportsResponse is returned from QueryReports().
struct QueryReportsResponse {
// status will be kOK on success or an error status on failure.
// If there was an error then the other fields of QueryReportsResponse
// should be ignored.
Status status;
// If status is kOK then this is the list of results of the query.
std::vector<ReportRecord> results;
// If status is kOK and pagination_token is not empty, it indicates that
// there were more results than could be returned in a single invocation
// of QueryReports(). Use this token as an input to another invocation
// of QueryReports() in order to obtain the next batch of results.
// Note that it is possible for pagination_token to be non-empty even if the
// number of results returned is fewer than the |max_results| specified in
// the query.
std::string pagination_token;
};
// Queries the ReportStore for the list of reports that exist for the
// given |customer_id|, |project_id|, |report_config_id|.
//
// |interval_start_time_seconds| and |interval_end_time_seconds| specify
// a half-open interval of |creation_time_seconds| that the query is
// restricted to. That is, the query will only return ReportRecords for which
// the |creation_time_seconds| field of the |report_id| is in the range
// [interval_start_time_seconds, interval_end_time_seconds).
//
// |max_results| must be positive and at most |max_results| will be returned.
// The number of returned results may be less than |max_results| for
// several reasons. The caller must look at whether or not the
// |pagination_token| in the returned QueryReportsResponse is empty in order
// to determine if there are further results that may be queried.
//
// If |pagination_token| is not empty then it should be the pagination_token
// from a QueryReportsResponse that was returned from a previous invocation of
// of this method with the same values for all of the other arguments.
// This query will be restricted to start after the last result returned from
// that previous query. A typical pattern is to invoke this method in a
// loop passing the pagination_token returned from one invocation into
// the following invocation. If pagination_token is not consistent with
// the other arguments then the returned status will be kInvalidArguments.
//
// See the comments on |QueryReportsResponse| for an explanation of how
// to interpret the response.
QueryReportsResponse QueryReports(uint32_t customer_id, uint32_t project_id,
uint32_t report_config_id,
int64_t interval_start_time_seconds,
int64_t interval_end_time_seconds,
size_t max_results,
std::string pagination_token);
// Permanently deletes all data in the ReportStore corresponding to the given
// report config.
Status DeleteAllForReportConfig(uint32_t customer_id, uint32_t project_id,
uint32_t report_config_id);
static std::string ToString(const ReportId& report_id);
// Sets the clock used by the ReportStore for obtaining the current time.
// Mostly useful for tests.
void set_clock(std::shared_ptr<util::ClockInterface> clock) {
clock_ = clock;
}
private:
friend class ReportStorePrivateTest;
friend class ReportStoreTestUtils;
// Makes all instantiations of ReportStoreAbstractTest friends.
template <class X>
friend class ReportStoreAbstractTest;
// Makes the row key for the report_metadata table that corresponds to the
// given |report_id|.
static std::string MakeMetadataRowKey(const ReportId& report_id);
// Makes the first possible row key for the report_metadata table for the
// given data.
static std::string MetadataRangeStartKey(uint32_t customer_id,
uint32_t project_id,
uint32_t report_config_id,
int64_t creation_time_seconds);
// Makes the first possible row key for the report_rows table for
// the given |report_id|.
static std::string ReportStartRowKey(const ReportId& report_id);
// Makes the last possible row key for the report_rows table for
// the given |report_id|.
static std::string ReportEndRowKey(const ReportId& report_id);
// Generates a new row key for the report_rows table for the report with
// the given report_id. Each time this method is invoked a new row key
// is generated.
static std::string GenerateReportRowKey(const ReportId& report_id);
// Makes the DataStore::Row that represents the arguments.
DataStore::Row MakeDataStoreRow(const ReportId& report_id,
const ReportMetadataLite& metadata);
// Write a row into the report_metadata table to represent the arguemnts.
Status WriteMetadata(const ReportId& report_id,
const ReportMetadataLite& metadata);
// Write many rows into the report_metadata table to represent the arguments.
// CHECK fails if |report_ids| and |metadata| do not have the same length.
Status WriteBulkMetadata(const std::vector<ReportId>& report_ids,
const std::vector<ReportMetadataLite>& metadata);
// The underlying data store.
const std::shared_ptr<DataStore> store_;
std::shared_ptr<util::ClockInterface> clock_;
};
} // namespace store
} // namespace analyzer
} // namespace cobalt
#endif // COBALT_ANALYZER_STORE_REPORT_STORE_H_