| // 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_ |