MXS-1512 Add test case for cache options
Here's now tested that the cache properly deals with the configuration setting "cache_in_transactions" and the current transaction state.
This commit is contained in:
18
server/modules/filter/cache/test/CMakeLists.txt
vendored
18
server/modules/filter/cache/test/CMakeLists.txt
vendored
@ -1,4 +1,5 @@
|
|||||||
include_directories(..)
|
include_directories(..)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../test)
|
||||||
|
|
||||||
add_library(cachetester
|
add_library(cachetester
|
||||||
tester.cc
|
tester.cc
|
||||||
@ -27,6 +28,21 @@ target_link_libraries(testrawstorage cachetester cache maxscale-common)
|
|||||||
add_executable(testlrustorage testlrustorage.cc)
|
add_executable(testlrustorage testlrustorage.cc)
|
||||||
target_link_libraries(testlrustorage cachetester cache maxscale-common)
|
target_link_libraries(testlrustorage cachetester cache maxscale-common)
|
||||||
|
|
||||||
|
add_executable(test_cacheoptions
|
||||||
|
test_cacheoptions.cc
|
||||||
|
|
||||||
|
../../test/filtermodule.cc
|
||||||
|
../../test/mock.cc
|
||||||
|
../../test/mock_backend.cc
|
||||||
|
../../test/mock_client.cc
|
||||||
|
../../test/mock_dcb.cc
|
||||||
|
../../test/mock_routersession.cc
|
||||||
|
../../test/mock_session.cc
|
||||||
|
../../test/module.cc
|
||||||
|
../../test/queryclassifiermodule.cc
|
||||||
|
)
|
||||||
|
target_link_libraries(test_cacheoptions maxscale-common)
|
||||||
|
|
||||||
add_test(TestCache_rules testrules)
|
add_test(TestCache_rules testrules)
|
||||||
|
|
||||||
add_test(TestCache_inmemory_keygeneration testkeygeneration storage_inmemory ${CMAKE_CURRENT_SOURCE_DIR}/input.test)
|
add_test(TestCache_inmemory_keygeneration testkeygeneration storage_inmemory ${CMAKE_CURRENT_SOURCE_DIR}/input.test)
|
||||||
@ -39,3 +55,5 @@ add_test(TestCache_storage_inmemory testrawstorage storage_inmemory 0 10 1000 10
|
|||||||
#usage: testlrustorage storage-module [threads [time [items [min-size [max-size]]]]]\n"
|
#usage: testlrustorage storage-module [threads [time [items [min-size [max-size]]]]]\n"
|
||||||
add_test(TestCache_lru_inmemory testlrustorage storage_inmemory 0 10 1000 1024 1024000)
|
add_test(TestCache_lru_inmemory testlrustorage storage_inmemory 0 10 1000 1024 1024000)
|
||||||
#add_test(TestCache_lru_rocksdb testlrustorage storage_rocksdb 0 10 1000 1024 1024000)
|
#add_test(TestCache_lru_rocksdb testlrustorage storage_rocksdb 0 10 1000 1024 1024000)
|
||||||
|
|
||||||
|
add_test(TestCache_options test_cacheoptions)
|
||||||
|
453
server/modules/filter/cache/test/test_cacheoptions.cc
vendored
Normal file
453
server/modules/filter/cache/test/test_cacheoptions.cc
vendored
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
/*
|
||||||
|
* 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/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2020-01-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 <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <maxscale/filtermodule.hh>
|
||||||
|
#include <maxscale/mock/backend.hh>
|
||||||
|
#include <maxscale/mock/client.hh>
|
||||||
|
#include <maxscale/mock/routersession.hh>
|
||||||
|
#include <maxscale/mock/session.hh>
|
||||||
|
#include "../cachefilter.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using maxscale::FilterModule;
|
||||||
|
namespace mock = maxscale::mock;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct CONFIG
|
||||||
|
{
|
||||||
|
bool stop_at_first_error;
|
||||||
|
} config =
|
||||||
|
{
|
||||||
|
true, // stop_at_first_error
|
||||||
|
};
|
||||||
|
|
||||||
|
// See https://github.com/mariadb-corporation/MaxScale/blob/2.2/Documentation/Filters/Cache.md#cache_inside_transactions
|
||||||
|
struct TEST_CASE
|
||||||
|
{
|
||||||
|
cache_in_trxs_t cit; /*< How to cache in transactions. */
|
||||||
|
mxs_session_trx_state_t trx_state; /*< The transaction state. */
|
||||||
|
bool should_use; /*< Whether the cache should be returned from the cache. */
|
||||||
|
} TEST_CASES[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
CACHE_IN_TRXS_NEVER,
|
||||||
|
SESSION_TRX_INACTIVE,
|
||||||
|
true // should_use
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CACHE_IN_TRXS_NEVER,
|
||||||
|
SESSION_TRX_ACTIVE,
|
||||||
|
false // should_use
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CACHE_IN_TRXS_NEVER,
|
||||||
|
SESSION_TRX_READ_ONLY,
|
||||||
|
false // should_use
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CACHE_IN_TRXS_READ_ONLY,
|
||||||
|
SESSION_TRX_INACTIVE,
|
||||||
|
true // should_use
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CACHE_IN_TRXS_READ_ONLY,
|
||||||
|
SESSION_TRX_ACTIVE,
|
||||||
|
false // should_use
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CACHE_IN_TRXS_READ_ONLY,
|
||||||
|
SESSION_TRX_READ_ONLY,
|
||||||
|
true // should_use
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CACHE_IN_TRXS_ALL,
|
||||||
|
SESSION_TRX_INACTIVE,
|
||||||
|
true // should_use
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CACHE_IN_TRXS_ALL,
|
||||||
|
SESSION_TRX_ACTIVE,
|
||||||
|
true // should_use
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CACHE_IN_TRXS_ALL,
|
||||||
|
SESSION_TRX_READ_ONLY,
|
||||||
|
true // should_use
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t N_TEST_CASES = sizeof(TEST_CASES) / sizeof(TEST_CASES[0]);
|
||||||
|
|
||||||
|
const char* to_string(cache_in_trxs_t x)
|
||||||
|
{
|
||||||
|
switch (x)
|
||||||
|
{
|
||||||
|
case CACHE_IN_TRXS_NEVER:
|
||||||
|
return "never";
|
||||||
|
|
||||||
|
case CACHE_IN_TRXS_READ_ONLY:
|
||||||
|
return "read_only_transactions";
|
||||||
|
|
||||||
|
case CACHE_IN_TRXS_ALL:
|
||||||
|
return "all_transactions";
|
||||||
|
|
||||||
|
default:
|
||||||
|
ss_dassert(!true);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream& operator << (ostream& out, cache_in_trxs_t x)
|
||||||
|
{
|
||||||
|
out << to_string(x);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream& operator << (ostream& out, mxs_session_trx_state_t trx_state)
|
||||||
|
{
|
||||||
|
out << session_trx_state_to_string(trx_state);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
static int counter = 0;
|
||||||
|
|
||||||
|
string create_unique_select()
|
||||||
|
{
|
||||||
|
stringstream ss;
|
||||||
|
ss << "SELECT col" << ++counter << " FROM tbl";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
int test(mock::Session& session,
|
||||||
|
FilterModule::Session& filter_session,
|
||||||
|
mock::RouterSession& router_session,
|
||||||
|
const TEST_CASE& tc)
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
mock::Client& client = session.client();
|
||||||
|
|
||||||
|
// Let's check that there's nothing pending.
|
||||||
|
ss_dassert(client.n_responses() == 0);
|
||||||
|
ss_dassert(router_session.idle());
|
||||||
|
|
||||||
|
session.set_trx_state(tc.trx_state);
|
||||||
|
session.set_autocommit(tc.trx_state == SESSION_TRX_INACTIVE);
|
||||||
|
|
||||||
|
string select(create_unique_select());
|
||||||
|
GWBUF* pStatement;
|
||||||
|
pStatement = mock::create_com_query(select);
|
||||||
|
|
||||||
|
cout << "Performing select: \"" << select << "\"" << flush;
|
||||||
|
filter_session.routeQuery(pStatement);
|
||||||
|
|
||||||
|
if (!router_session.idle())
|
||||||
|
{
|
||||||
|
cout << ", reached backend." << endl;
|
||||||
|
|
||||||
|
// Let's cause the backend to respond.
|
||||||
|
router_session.respond();
|
||||||
|
|
||||||
|
// And let's verify that the backend is now empty...
|
||||||
|
ss_dassert(router_session.idle());
|
||||||
|
// ...and that we have received a response.
|
||||||
|
ss_dassert(client.n_responses() == 1);
|
||||||
|
|
||||||
|
// Let's do the select again.
|
||||||
|
pStatement = mock::create_com_query(select);
|
||||||
|
|
||||||
|
cout << "Performing same select: \"" << select << "\"" << flush;
|
||||||
|
filter_session.routeQuery(pStatement);
|
||||||
|
|
||||||
|
if (tc.should_use)
|
||||||
|
{
|
||||||
|
if (!router_session.idle())
|
||||||
|
{
|
||||||
|
cout << "\nERROR: Select reached backend and was not provided from cache." << endl;
|
||||||
|
router_session.respond();
|
||||||
|
++rv;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << ", cache was used." << endl;
|
||||||
|
|
||||||
|
// Let's check we did receive a response.
|
||||||
|
ss_dassert(client.n_responses() == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (router_session.idle())
|
||||||
|
{
|
||||||
|
cout << "\nERROR: Select was provided from cache and did not reach backend." << endl;
|
||||||
|
++rv;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << ", reached backend." << endl;
|
||||||
|
router_session.respond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tc.trx_state != SESSION_TRX_INACTIVE) && (tc.trx_state != SESSION_TRX_READ_ONLY))
|
||||||
|
{
|
||||||
|
// A transaction, but not a read-only one.
|
||||||
|
|
||||||
|
string update("UPDATE tbl SET a=1;");
|
||||||
|
pStatement = mock::create_com_query("UPDATE tbl SET a=1;");
|
||||||
|
|
||||||
|
cout << "Performing update: \"" << update << "\"" << flush;
|
||||||
|
filter_session.routeQuery(pStatement);
|
||||||
|
|
||||||
|
if (router_session.idle())
|
||||||
|
{
|
||||||
|
cout << "\n"
|
||||||
|
<< "ERROR: Did not reach backend." << endl;
|
||||||
|
++rv;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << ", reached backend." << endl;
|
||||||
|
router_session.respond();
|
||||||
|
|
||||||
|
// Let's make the select again.
|
||||||
|
pStatement = mock::create_com_query(select);
|
||||||
|
|
||||||
|
cout << "Performing select: \"" << select << "\"" << flush;
|
||||||
|
filter_session.routeQuery(pStatement);
|
||||||
|
|
||||||
|
if (router_session.idle())
|
||||||
|
{
|
||||||
|
cout << "\nERROR: Did not reach backend." << endl;
|
||||||
|
++rv;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The select reached the backend, i.e. the cache was not used after
|
||||||
|
// a non-SELECT.
|
||||||
|
cout << ", reached backend." << endl;
|
||||||
|
router_session.respond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Irrespective of what was going on above, the cache should now contain the
|
||||||
|
// original select. So, let's do a select with no transaction.
|
||||||
|
|
||||||
|
cout << "Setting transaction state to SESSION_TRX_INACTIVE" << endl;
|
||||||
|
session.set_trx_state(SESSION_TRX_INACTIVE);
|
||||||
|
session.set_autocommit(true);
|
||||||
|
|
||||||
|
pStatement = mock::create_com_query(select);
|
||||||
|
|
||||||
|
cout << "Performing select: \"" << select << "\"" << flush;
|
||||||
|
filter_session.routeQuery(pStatement);
|
||||||
|
|
||||||
|
if (router_session.idle())
|
||||||
|
{
|
||||||
|
cout << ", cache was used." << endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << "\nERROR: cache was not used." << endl;
|
||||||
|
router_session.respond();
|
||||||
|
++rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << "\nERROR: Did not reach backend." << endl;
|
||||||
|
++rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test(FilterModule::Instance& filter_instance, const TEST_CASE& tc)
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
mock::ResultSetBackend backend;
|
||||||
|
mock::RouterSession router_session(&backend);
|
||||||
|
|
||||||
|
mock::Client client("bob", "127.0.0.1");
|
||||||
|
mock::Session session(&client);
|
||||||
|
|
||||||
|
auto_ptr<FilterModule::Session> sFilter_session = filter_instance.newSession(&session);
|
||||||
|
|
||||||
|
if (sFilter_session.get())
|
||||||
|
{
|
||||||
|
router_session.set_as_downstream_on(sFilter_session.get());
|
||||||
|
|
||||||
|
client.set_as_upstream_on(*sFilter_session.get());
|
||||||
|
|
||||||
|
rv += test(session, *sFilter_session.get(), router_session, tc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test(FilterModule& filter_module, const TEST_CASE& tc)
|
||||||
|
{
|
||||||
|
int rv = 1;
|
||||||
|
|
||||||
|
auto_ptr<FilterModule::ConfigParameters> sParameters = filter_module.create_default_parameters();
|
||||||
|
sParameters->set_value("cache_in_transactions", to_string(tc.cit));
|
||||||
|
sParameters->set_value("debug", "31");
|
||||||
|
|
||||||
|
auto_ptr<FilterModule::Instance> sInstance = filter_module.createInstance("test", NULL, sParameters);
|
||||||
|
|
||||||
|
if (sInstance.get())
|
||||||
|
{
|
||||||
|
rv = test(*sInstance, tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
int run()
|
||||||
|
{
|
||||||
|
int rv = 1;
|
||||||
|
|
||||||
|
auto_ptr<FilterModule> sModule = FilterModule::load("cache");
|
||||||
|
|
||||||
|
if (sModule.get())
|
||||||
|
{
|
||||||
|
if (maxscale::Module::process_init())
|
||||||
|
{
|
||||||
|
if (maxscale::Module::thread_init())
|
||||||
|
{
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < N_TEST_CASES; ++i)
|
||||||
|
{
|
||||||
|
const TEST_CASE& tc = TEST_CASES[i];
|
||||||
|
|
||||||
|
cout << "CIT: " << tc.cit
|
||||||
|
<< ", TRX_STATE: " << tc.trx_state
|
||||||
|
<< ", should use: " << tc.should_use
|
||||||
|
<< endl;
|
||||||
|
|
||||||
|
rv += test(*sModule.get(), tc);
|
||||||
|
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
|
if ((rv != 0) && config.stop_at_first_error)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxscale::Module::thread_finish();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "error: Could not perform thread initialization." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxscale::Module::process_finish();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "error: Could not perform process initialization." << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "error: Could not load filter module." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
char USAGE[] =
|
||||||
|
"usage: test_dbfwfilter [-d]\n"
|
||||||
|
"\n"
|
||||||
|
"-d don't stop at first error\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
int c;
|
||||||
|
while ((c = getopt(argc, argv, "d")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'd':
|
||||||
|
config.stop_at_first_error = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
rv = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rv == 0)
|
||||||
|
{
|
||||||
|
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
|
||||||
|
{
|
||||||
|
if (qc_setup("qc_sqlite", QC_SQL_MODE_DEFAULT, NULL))
|
||||||
|
{
|
||||||
|
if (qc_process_init(QC_INIT_SELF))
|
||||||
|
{
|
||||||
|
rv = run();
|
||||||
|
|
||||||
|
cout << rv << " failures." << endl;
|
||||||
|
|
||||||
|
qc_process_end(QC_INIT_SELF);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "error: Could not initialize query classifier." << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "error: Could not setup query classifier." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
mxs_log_finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << USAGE << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
Reference in New Issue
Block a user