From 892538e233af49ef8913de74debab3afd19d1002 Mon Sep 17 00:00:00 2001 From: Vaidas Gasiunas Date: Fri, 4 Mar 2022 20:04:17 +0100 Subject: [PATCH] ApiTester: Adding some comments to the headers --- bindings/c/test/apitester/TesterScheduler.h | 12 ++++ bindings/c/test/apitester/TesterTestSpec.h | 30 +++++++++- .../apitester/TesterTransactionExecutor.h | 47 +++++++++++++++- bindings/c/test/apitester/TesterWorkload.h | 55 ++++++++++++++++++- 4 files changed, 139 insertions(+), 5 deletions(-) diff --git a/bindings/c/test/apitester/TesterScheduler.h b/bindings/c/test/apitester/TesterScheduler.h index 491aef568b..c48183b3e0 100644 --- a/bindings/c/test/apitester/TesterScheduler.h +++ b/bindings/c/test/apitester/TesterScheduler.h @@ -32,15 +32,27 @@ using TTaskFct = std::function; extern const TTaskFct NO_OP_TASK; +/** + * Scheduler for asynchronous execution of tasks on a pool of threads + */ class IScheduler { public: virtual ~IScheduler() {} + + // Create scheduler threads and begin accepting tasks virtual void start() = 0; + + // Schedule a task for asynchronous execution virtual void schedule(TTaskFct task) = 0; + + // Gracefully stop the scheduler. Waits for already running tasks to be finish virtual void stop() = 0; + + // Join with all threads of the scheduler virtual void join() = 0; }; +// create a scheduler using given number of threads std::unique_ptr createScheduler(int numThreads); } // namespace FdbApiTester diff --git a/bindings/c/test/apitester/TesterTestSpec.h b/bindings/c/test/apitester/TesterTestSpec.h index 5c7c162756..7467a7d59a 100644 --- a/bindings/c/test/apitester/TesterTestSpec.h +++ b/bindings/c/test/apitester/TesterTestSpec.h @@ -31,32 +31,58 @@ namespace FdbApiTester { +/// Workload specification struct WorkloadSpec { std::string name; std::unordered_map options; }; +// Test speficification loaded from a *.toml file struct TestSpec { + // Title of the test std::string title; - // api version, using the latest version by default + + // FDB API version, using the latest version by default int apiVersion = FDB_API_VERSION; + + // Use blocking waits on futures instead of scheduling callbacks bool blockOnFutures = false; + + // Use multi-threaded FDB client bool multiThreaded = false; + + // Enable injection of errors in FDB client bool buggify = false; + + // Execute future callbacks on the threads of the external FDB library + // rather than on the main thread of the local FDB client library bool fdbCallbacksOnExternalThreads = false; + + // Execute each transaction in a separate database instance bool databasePerTransaction = false; + + // Size of the FDB client thread pool (a random number in the [min,max] range) int minFdbThreads = 1; int maxFdbThreads = 1; + + // Size of the thread pool for test workloads (a random number in the [min,max] range) int minClientThreads = 1; int maxClientThreads = 1; + + // Size of the database instance pool (a random number in the [min,max] range) + // Each transaction is assigned randomly to one of the databases in the pool int minDatabases = 1; int maxDatabases = 1; + + // Number of workload clients (a random number in the [min,max] range) int minClients = 1; int maxClients = 10; - std::string testFile; + + // List of workloads with their options std::vector workloads; }; +// Read the test specfication from a *.toml file TestSpec readTomlTestSpec(std::string fileName); } // namespace FdbApiTester diff --git a/bindings/c/test/apitester/TesterTransactionExecutor.h b/bindings/c/test/apitester/TesterTransactionExecutor.h index 899f812cfc..3797d6df9e 100644 --- a/bindings/c/test/apitester/TesterTransactionExecutor.h +++ b/bindings/c/test/apitester/TesterTransactionExecutor.h @@ -31,27 +31,55 @@ namespace FdbApiTester { +/** + * Interface to be used for implementation of a concrete transaction + */ class ITransactionContext { public: virtual ~ITransactionContext() {} + + // Current FDB transaction virtual Transaction* tx() = 0; + + // Schedule a continuation to be executed when the future gets ready virtual void continueAfter(Future f, TTaskFct cont) = 0; + + // Commit the transaction virtual void commit() = 0; + + // Mark the transaction as completed without committing it (for read transactions) virtual void done() = 0; + + // A continuation to be executed when all of the given futures get ready virtual void continueAfterAll(std::shared_ptr> futures, TTaskFct cont); }; +/** + * Interface of an actor object implementing a concrete transaction + */ class ITransactionActor { public: virtual ~ITransactionActor() {} + + // Initialize with the given transaction context virtual void init(ITransactionContext* ctx) = 0; + + // Start execution of the transaction, also called on retries virtual void start() = 0; + + // Reset the transaction state virtual void reset() = 0; + + // Abort the transaction with an unretriable error virtual void setError(fdb_error_t err) { error = err; } + // Unretriable error, set if the transaction has failed fdb_error_t error = error_code_success; }; +/** + * A helper base class for transaction actors + */ class TransactionActorBase : public ITransactionActor { public: void init(ITransactionContext* ctx) override { context = ctx; } @@ -66,8 +94,12 @@ private: ITransactionContext* context = nullptr; }; +// Type of the lambda functions implementing a transaction using TTxStartFct = std::function; +/** + * A wrapper class for transactions implemented by lambda functions + */ class TransactionFct : public TransactionActorBase { public: TransactionFct(TTxStartFct startFct) : startFct(startFct) {} @@ -77,13 +109,25 @@ private: TTxStartFct startFct; }; +/** + * Configuration of transaction execution mode + */ struct TransactionExecutorOptions { - std::string prefix = ""; + // Use blocking waits on futures bool blockOnFutures = false; + + // Create each transaction in a separate database instance bool databasePerTransaction = false; + + // The size of the database instance pool int numDatabases = 1; }; +/** + * Transaction executor provides an interface for executing transactions + * It is responsible for instantiating FDB databases and transactions and managing their lifecycle + * according to the provided options + */ class ITransactionExecutor { public: virtual ~ITransactionExecutor() {} @@ -91,6 +135,7 @@ public: virtual void execute(std::shared_ptr tx, TTaskFct cont) = 0; }; +// Create a transaction executor for the given options std::unique_ptr createTransactionExecutor(const TransactionExecutorOptions& options); } // namespace FdbApiTester diff --git a/bindings/c/test/apitester/TesterWorkload.h b/bindings/c/test/apitester/TesterWorkload.h index aa4b5cc3ca..023a443757 100644 --- a/bindings/c/test/apitester/TesterWorkload.h +++ b/bindings/c/test/apitester/TesterWorkload.h @@ -34,19 +34,33 @@ namespace FdbApiTester { class WorkloadManager; +// Workoad interface class IWorkload { public: virtual ~IWorkload() {} + + // Intialize the workload virtual void init(WorkloadManager* manager) = 0; + + // Start executing the workload virtual void start() = 0; }; +// Workload configuration struct WorkloadConfig { + // Workoad name std::string name; + + // Client ID assigned to the workload (a number from 0 to numClients-1) int clientId; + + // Total number of clients int numClients; + + // Workload options: as key-value pairs std::unordered_map options; + // Get option of a certain type by name. Throws an exception if the values is of a wrong type int getIntOption(const std::string& name, int defaultVal) const; double getFloatOption(const std::string& name, double defaultVal) const; }; @@ -56,6 +70,8 @@ struct WorkloadConfig { class WorkloadBase : public IWorkload { public: WorkloadBase(const WorkloadConfig& config); + + // Initialize the workload void init(WorkloadManager* manager) override; protected: @@ -65,12 +81,12 @@ protected: // Execute a transaction within the workload void execTransaction(std::shared_ptr tx, TTaskFct cont, bool failOnError = true); - // Execute a transaction within the workload, a convenience method for tranasactions defined by a single lambda + // Execute a transaction within the workload, a convenience method for a tranasaction defined by a lambda function void execTransaction(TTxStartFct start, TTaskFct cont, bool failOnError = true) { execTransaction(std::make_shared(start), cont, failOnError); } - // Log an error message + // Log an error message, increase error counter void error(const std::string& msg); // Log an info message @@ -85,13 +101,24 @@ private: // Keep track of tasks scheduled by the workload // End workload when this number falls to 0 std::atomic tasksScheduled; + + // Number of errors logged std::atomic numErrors; protected: + // Client ID assigned to the workload (a number from 0 to numClients-1) int clientId; + + // Total number of clients int numClients; + + // The maximum number of errors before stoppoing the workload int maxErrors; + + // Workload identifier, consisting of workload name and client ID std::string workloadId; + + // Workload is failed, no further transactions or continuations will be scheduled by the workload std::atomic failed; }; @@ -109,6 +136,7 @@ public: // Run all workloads. Blocks until all workloads complete void run(); + // True if at least one workload has failed bool failed() { std::unique_lock lock(mutex); return numWorkloadsFailed > 0; @@ -117,29 +145,52 @@ public: private: friend WorkloadBase; + // Info about a running workload struct WorkloadInfo { + // Reference to the workoad for ownership std::shared_ptr ref; + // Continuation to be executed after completing the workload TTaskFct cont; }; + // To be called by a workload to notify that it is done void workloadDone(IWorkload* workload, bool failed); + // Transaction executor to be used by the workloads ITransactionExecutor* txExecutor; + + // A scheduler to be used by the workloads IScheduler* scheduler; + // Mutex protects access to workloads & numWorkloadsFailed std::mutex mutex; + + // A map of currently running workloads std::unordered_map workloads; + + // Number of workloads failed int numWorkloadsFailed; }; +// A workload factory struct IWorkloadFactory { + // create a workload by name static std::shared_ptr create(std::string const& name, const WorkloadConfig& config); + + // a singleton registry of workload factories static std::unordered_map& factories(); + // Interface to be implemented by a workload factory virtual ~IWorkloadFactory() = default; virtual std::shared_ptr create(const WorkloadConfig& config) = 0; }; +/** + * A template for a workload factory for creating workloads of a certain type + * + * Declare a global instance of the factory for a workload type as follows: + * WorkloadFactory MyWorkloadFactory("myWorkload"); + */ template struct WorkloadFactory : IWorkloadFactory { WorkloadFactory(const char* name) { factories()[name] = this; }