diff --git a/src/metrics/include/metrics/PostCondition.hpp b/src/metrics/include/metrics/PostCondition.hpp index 1f616c9447..dc00efdcde 100644 --- a/src/metrics/include/metrics/PostCondition.hpp +++ b/src/metrics/include/metrics/PostCondition.hpp @@ -29,10 +29,35 @@ enum class MetricRequirement { kBytes, }; -enum class Comparison { - kGT, -}; - +/** + * A PostCondition added to a CrudActor Operation checks the metrics of the operation immediately + * after it runs, so that it can be marked as failing if it does not meet expectations for the + * intended test scenario. For example, a post-condition ensuring that a find command is returning + * the expected number of documents can quickly identify a spurious performance improvement caused + * by a bug in query evaluation. A post-condition can also check the state of a collection, ensuring + * that it is large enough to appropriately stress target code paths, when attached to a query that + * scans the entire collection. + * + * Example: + * ```yaml + * Actors: + * - Name: InsertOne + * Type: CrudActor + * Database: test + * Phases: + * - Collection: test + * Operations: + * - OperationName: insertOne + * OperationCommand: + * Document: {a: "value"} + * PostCondition: + * - Metric: documents + * EQ: 1 + * - Metric: bytes + * LT: 20 + * GT: 5 + * ``` + */ struct PostCondition { // The default PostCondition is a tautology. PostCondition() = default; @@ -47,6 +72,10 @@ struct PostCondition { } } + /** + * Checks if the 'ops' and 'bytes' metrics for the execution of a CRUD operation meet the + * requirements and throws an exception if they do not. + */ void check(long long ops, long long bytes) const { for (const auto& req : requirements) { long long observedValue = [&]() { @@ -70,33 +99,48 @@ struct PostCondition { } private: - struct Predicate { + /** + * A binary comparison relation on 'long long' ints (e.g., ==, <) + */ + struct Relation { + // The YAML key used to include this relation in a PostCondition specification. std::string key; + + // The symbol to use when outputting user messages about a comparison result. std::string symbol; + + // Implementation that returns true if the relation holds (meeting the post-condition + // requirement) for a pair of values. std::function evaluateFn; }; struct Requirement { MetricRequirement metric; - const Predicate& predicate; + const Relation& relation; long long requiredValue; Requirement(MetricRequirement metric, const Predicate& predicate, long long requiredValue) - : metric(metric), predicate(predicate), requiredValue(requiredValue) {} + : metric(metric), relation(relation), requiredValue(requiredValue) {} }; + /** + * Parse one block from the list of blocks in a YAML PostCondition into one or more entries in + * the 'requirements' list. + */ void addCondition(const Node& node) { static const char* metricKey = "Metric"; static const char* documentMetric = "documents"; static const char* bytesMetric = "bytes"; - static const std::vector predicates = { + // The parser uses this table to translate the YAML key (e.g., "EQ") for a comparison into a + // function that can perform the comparison. + static const std::vector relations = { {"EQ", "==", [](long long left, long long right) { return left == right; }}, {"NE", "!=", [](long long left, long long right) { return left != right; }}, - {"LT", "==", [](long long left, long long right) { return left < right; }}, - {"LTE", "==", [](long long left, long long right) { return left <= right; }}, - {"GT", "==", [](long long left, long long right) { return left > right; }}, - {"GTE", "==", [](long long left, long long right) { return left >= right; }}, + {"LT", "<", [](long long left, long long right) { return left < right; }}, + {"LTE", "<=", [](long long left, long long right) { return left <= right; }}, + {"GT", ">", [](long long left, long long right) { return left > right; }}, + {"GTE", ">=", [](long long left, long long right) { return left >= right; }}, }; auto metricName = node[metricKey].maybe(); @@ -112,13 +156,18 @@ struct PostCondition { BOOST_THROW_EXCEPTION(InvalidConfigurationException("Unexpected metric")); } - for (const auto& predicate : predicates) { - if (auto requiredValue = node[predicate.key].maybe()) { - requirements.emplace_back(metric, predicate, *requiredValue); + for (const auto& relation : relations) { + if (auto requiredValue = node[relation.key].maybe()) { + requirements.emplace_back(metric, relation, *requiredValue); } } } + /* + * A list of requirements that must all be met for the post-condition to be fulfilled. Each + * requirement specifies the metric to check, a reference value to compare the metric to, and an + * arithmetic relation (e.g, ==, <) to compare with. + */ std::vector requirements; };