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:
parent
b225eeff2c
commit
481892b452
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(${CMAKE_CURRENT_SOURCE_DIR}/../../test)
|
||||
|
||||
add_library(cachetester
|
||||
tester.cc
|
||||
@ -27,6 +28,21 @@ target_link_libraries(testrawstorage cachetester cache maxscale-common)
|
||||
add_executable(testlrustorage testlrustorage.cc)
|
||||
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_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"
|
||||
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_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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user