From eda84a1f9626d513a167daeecd91589df21527d3 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Thu, 15 Dec 2016 17:16:36 +0200 Subject: [PATCH] Cache: Add TesterStorage class A class dedicated for performing basic testing of Storages. --- .../filter/cache/test/testerstorage.cc | 219 ++++++++++++++++++ .../filter/cache/test/testerstorage.hh | 152 ++++++++++++ 2 files changed, 371 insertions(+) create mode 100644 server/modules/filter/cache/test/testerstorage.cc create mode 100644 server/modules/filter/cache/test/testerstorage.hh diff --git a/server/modules/filter/cache/test/testerstorage.cc b/server/modules/filter/cache/test/testerstorage.cc new file mode 100644 index 000000000..737c42682 --- /dev/null +++ b/server/modules/filter/cache/test/testerstorage.cc @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "testerstorage.hh" +#include +#include +#include "storage.hh" + +using namespace std; + +// +// class TesterStorage::HitTask +// + +TesterStorage::HitTask::HitTask(ostream* pOut, + Storage* pStorage, + const CacheItems* pCache_items) + : Tester::Task(pOut) + , m_storage(*pStorage) + , m_cache_items(*pCache_items) + , m_puts(0) + , m_gets(0) + , m_dels(0) + , m_misses(0) +{ + ss_dassert(m_cache_items.size() > 0); +} + +int TesterStorage::HitTask::run() +{ + int rv = EXIT_SUCCESS; + + size_t n = m_cache_items.size(); + size_t i = 0; + + while (!should_terminate()) + { + if (i >= n) + { + i = 0; + } + + const CacheItems::value_type& cache_item = m_cache_items[i]; + + storage_action_t action = TesterStorage::get_random_action(); + + switch (action) + { + case STORAGE_PUT: + { + cache_result_t result = m_storage.put_value(cache_item.first, cache_item.second); + if (result == CACHE_RESULT_OK) + { + ++m_puts; + } + else + { + ss_dassert(!true); + rv = EXIT_FAILURE; + } + } + break; + + case STORAGE_GET: + { + GWBUF* pQuery; + cache_result_t result = m_storage.get_value(cache_item.first, 0, &pQuery); + + if (result == CACHE_RESULT_OK) + { + ss_dassert(GWBUF_LENGTH(pQuery) == GWBUF_LENGTH(cache_item.second)); + ss_dassert(memcmp(GWBUF_DATA(pQuery), GWBUF_DATA(cache_item.second), + GWBUF_LENGTH(pQuery)) == 0); + + gwbuf_free(pQuery); + ++m_gets; + } + else if (result == CACHE_RESULT_NOT_FOUND) + { + ++m_misses; + } + else + { + ss_dassert(!true); + rv = EXIT_FAILURE; + } + } + break; + + case STORAGE_DEL: + { + cache_result_t result = m_storage.del_value(cache_item.first); + + if (result == CACHE_RESULT_OK) + { + ++m_dels; + } + else if (result == CACHE_RESULT_NOT_FOUND) + { + ++m_misses; + } + else + { + ss_dassert(!true); + rv = EXIT_FAILURE; + } + } + break; + + default: + ss_dassert(!true); + } + + ++i; + } + + stringstream ss; + ss << "HitTask ending: " + << m_gets << ", " << m_puts << ", " << m_dels << ", " << m_misses << "\n"; + + out() << ss.str() << flush; + + return rv; +} + +// +// class TesterStorage +// + +TesterStorage::TesterStorage(std::ostream* pOut, StorageFactory* pFactory) + : Tester(pOut) + , m_factory(*pFactory) +{ +} + +int TesterStorage::run(size_t n_threads, size_t n_seconds, std::istream& in) +{ + int rv = EXIT_FAILURE; + + Storage* pStorage = get_storage(); + + if (pStorage) + { + size_t n_items = get_n_items(n_threads, n_seconds); + + CacheItems cache_items; + + if (get_cache_items(in, n_items, *pStorage, &cache_items)) + { + rv = run(n_threads, n_seconds, cache_items, *pStorage); + } + + delete pStorage; + } + + return rv; +} + +int TesterStorage::run(size_t n_threads, + size_t n_seconds, + const CacheItems& cache_items, + Storage& storage) +{ + return run_hit_task(n_threads, n_seconds, cache_items, storage); +} + +int TesterStorage::run_hit_task(size_t n_threads, + size_t n_seconds, + const CacheItems& cache_items, + Storage& storage) +{ + int rv = EXIT_FAILURE; + + Tasks tasks; + + for (size_t i = 0; i < n_threads; ++i) + { + tasks.push_back(new HitTask(&out(), &storage, &cache_items)); + } + + rv = Tester::run(out(), n_seconds, tasks); + + for_each(tasks.begin(), tasks.end(), Task::free); + + return rv; +} + +// static +TesterStorage::storage_action_t TesterStorage::get_random_action() +{ + storage_action_t action; + long l = random(); + + if (l < RAND_MAX / 3) + { + action = STORAGE_PUT; + } + else if (l < 2 * (RAND_MAX / 3)) + { + action = STORAGE_GET; + } + else + { + action = STORAGE_DEL; + } + + return action; +} + diff --git a/server/modules/filter/cache/test/testerstorage.hh b/server/modules/filter/cache/test/testerstorage.hh new file mode 100644 index 000000000..617b4e335 --- /dev/null +++ b/server/modules/filter/cache/test/testerstorage.hh @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include +#include "tester.hh" + +class StorageFactory; + +class TesterStorage : public Tester +{ +public: + enum storage_action_t + { + STORAGE_PUT, /*< Put an item to the storage. */ + STORAGE_GET, /*< Get an item from the storage. */ + STORAGE_DEL /*< Delete an item from the storage. */ + }; + + /** + * @class HitTask + * + * A task whose sole purpose is to hit a Storage continuously + * and intensly. + */ + class HitTask : public Tester::Task + { + public: + /** + * Constructor + * + * @param pOut The stream to use for (user) output. + * @param pStorage The storage to hit. + * @param pCache_items The cache items to use when hitting the storage. + */ + HitTask(std::ostream* pOut, + Storage* pStorage, + const CacheItems* pCache_items); + + /** + * Runs continuously until the task is terminated. + * + * @return EXIT_SUCCESS or EXIT_FAILURE + */ + int run(); + + private: + HitTask(const HitTask&); + HitTask& operator = (const HitTask&); + + private: + Storage& m_storage; /*< The storage that is hit. */ + const CacheItems& m_cache_items; /*< The cache items that are used. */ + size_t m_puts; /*< How many puts. */ + size_t m_gets; /*< How many gets. */ + size_t m_dels; /*< How many deletes. */ + size_t m_misses; /*< How many misses. */ + }; + + /** + * Reads statements from the provided stream, converts them to cache items and + * runs all storage tasks using as many threads as specified for the specified + * number of seconds. + * + * @param n_threads How many threads to use. + * @param n_seconds For how many seconds to run the test. + * @param in Stream, assumed to refer to a file containing statements. + * + * @return EXIT_SUCCESS or EXIT_FAILURE. + */ + virtual int run(size_t n_threads, size_t n_seconds, std::istream& in); + + /** + * Runs all storage tasks using as many threads as specified, for the specified + * number of seconds. + * + * @param n_threads How many threads to use. + * @param n_seconds For how many seconds to run the test. + * @param cache_items The cache items to use. Assumed to have been created using + * @c storage. + * @param storage The storage items to use. + * + * @return EXIT_SUCCESS or EXIT_FAILURE. + */ + virtual int run(size_t n_threads, size_t n_seconds, const CacheItems& cache_items, Storage& storage); + + /** + * Runs the HitTask using as many threads as specified, for the specified + * number of seconds. + * + * @param n_threads How many threads to use. + * @param n_seconds For how many seconds to run the test. + * @param cache_items The cache items to use. Assumed to have been created using + * @c storage. + * @param storage The storage items to use. + * + * @return EXIT_SUCCESS or EXIT_FAILURE. + */ + virtual int run_hit_task(size_t n_threads, + size_t n_seconds, + const CacheItems& cache_items, + Storage& storage); + + /** + * Get a random action. + * + * @return Some storage action. + */ + static storage_action_t get_random_action(); + +protected: + /** + * Constructor + * + * @param pOut Pointer to the stream to be used for (user) output. + * @param pFactory Pointer to factory to be used. + */ + TesterStorage(std::ostream* pOut, StorageFactory* pFactory); + + /** + * Return a storage instance. The ownership is transferred to the caller. + * + * @return A storage instance or NULL. + */ + virtual Storage* get_storage() = 0; + + /** + * Return the desired number of cache items to be used in the tests. + * + * @param n_threads How many threads are used. + * @param n_seconds For how many seconds the tests will be run. + * + * @return The desired number of items to use. + */ + virtual size_t get_n_items(size_t n_threads, size_t n_seconds) = 0; + +protected: + StorageFactory& m_factory; /*< The storage factory that is used. */ + +private: + TesterStorage(const TesterStorage&); + TesterStorage& operator = (const TesterStorage&); +};