2017-05-26 04:48:44 +08:00
|
|
|
/*
|
|
|
|
* ConfigureDatabase.actor.cpp
|
|
|
|
*
|
|
|
|
* This source file is part of the FoundationDB open source project
|
|
|
|
*
|
|
|
|
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
2018-02-22 02:25:11 +08:00
|
|
|
*
|
2017-05-26 04:48:44 +08:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2018-02-22 02:25:11 +08:00
|
|
|
*
|
2017-05-26 04:48:44 +08:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2018-02-22 02:25:11 +08:00
|
|
|
*
|
2017-05-26 04:48:44 +08:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2019-02-18 07:41:16 +08:00
|
|
|
#include "fdbclient/NativeAPI.actor.h"
|
2019-02-18 11:25:16 +08:00
|
|
|
#include "fdbserver/TesterInterface.actor.h"
|
2019-02-18 09:38:13 +08:00
|
|
|
#include "fdbclient/ManagementAPI.actor.h"
|
2019-02-18 11:18:30 +08:00
|
|
|
#include "fdbserver/workloads/workloads.actor.h"
|
2017-05-26 04:48:44 +08:00
|
|
|
#include "fdbrpc/simulator.h"
|
2018-08-11 06:18:24 +08:00
|
|
|
#include "flow/actorcompiler.h" // This must be the last #include.
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
// "ssd" is an alias to the preferred type which skews the random distribution toward it but that's okay.
|
2019-03-19 06:10:04 +08:00
|
|
|
static const char* storeTypes[] = { "ssd", "ssd-1", "ssd-2", "memory", "memory-1", "memory-2" };
|
2019-07-09 13:22:45 +08:00
|
|
|
static const char* logTypes[] = {
|
|
|
|
"log_engine:=1", "log_engine:=2",
|
|
|
|
"log_spill:=1", "log_spill:=2",
|
|
|
|
"log_version:=2", "log_version:=3", "log_version:=4"
|
|
|
|
};
|
2017-05-26 04:48:44 +08:00
|
|
|
static const char* redundancies[] = { "single", "double", "triple" };
|
|
|
|
|
2018-11-13 09:40:40 +08:00
|
|
|
std::string generateRegions() {
|
2018-11-05 11:53:55 +08:00
|
|
|
std::string result;
|
2019-05-11 05:01:52 +08:00
|
|
|
if(g_simulator.physicalDatacenters == 1 || (g_simulator.physicalDatacenters == 2 && deterministicRandom()->random01() < 0.25) || g_simulator.physicalDatacenters == 3) {
|
2018-11-05 11:53:55 +08:00
|
|
|
return " usable_regions=1 regions=\"\"";
|
|
|
|
}
|
|
|
|
|
2019-05-11 05:01:52 +08:00
|
|
|
if(deterministicRandom()->random01() < 0.25) {
|
|
|
|
return format(" usable_regions=%d", deterministicRandom()->randomInt(1,3));
|
2018-11-13 09:40:40 +08:00
|
|
|
}
|
|
|
|
|
2018-11-05 12:11:23 +08:00
|
|
|
int primaryPriority = 1;
|
|
|
|
int remotePriority = -1;
|
2019-05-11 05:01:52 +08:00
|
|
|
double priorityType = deterministicRandom()->random01();
|
2018-11-05 12:11:23 +08:00
|
|
|
if(priorityType < 0.1) {
|
|
|
|
primaryPriority = -1;
|
|
|
|
remotePriority = 1;
|
|
|
|
} else if(priorityType < 0.2) {
|
|
|
|
remotePriority = 1;
|
|
|
|
primaryPriority = 1;
|
|
|
|
}
|
|
|
|
|
2018-11-05 11:53:55 +08:00
|
|
|
StatusObject primaryObj;
|
|
|
|
StatusObject primaryDcObj;
|
|
|
|
primaryDcObj["id"] = "0";
|
2018-11-05 12:11:23 +08:00
|
|
|
primaryDcObj["priority"] = primaryPriority;
|
2018-11-05 11:53:55 +08:00
|
|
|
StatusArray primaryDcArr;
|
|
|
|
primaryDcArr.push_back(primaryDcObj);
|
|
|
|
|
|
|
|
StatusObject remoteObj;
|
|
|
|
StatusObject remoteDcObj;
|
|
|
|
remoteDcObj["id"] = "1";
|
2018-11-05 12:11:23 +08:00
|
|
|
remoteDcObj["priority"] = remotePriority;
|
2018-11-05 11:53:55 +08:00
|
|
|
StatusArray remoteDcArr;
|
|
|
|
remoteDcArr.push_back(remoteDcObj);
|
|
|
|
|
2019-05-11 05:01:52 +08:00
|
|
|
if(g_simulator.physicalDatacenters > 3 && deterministicRandom()->random01() < 0.5) {
|
2018-11-05 11:53:55 +08:00
|
|
|
StatusObject primarySatelliteObj;
|
|
|
|
primarySatelliteObj["id"] = "2";
|
|
|
|
primarySatelliteObj["priority"] = 1;
|
|
|
|
primarySatelliteObj["satellite"] = 1;
|
|
|
|
primaryDcArr.push_back(primarySatelliteObj);
|
|
|
|
|
|
|
|
StatusObject remoteSatelliteObj;
|
|
|
|
remoteSatelliteObj["id"] = "3";
|
|
|
|
remoteSatelliteObj["priority"] = 1;
|
|
|
|
remoteSatelliteObj["satellite"] = 1;
|
|
|
|
remoteDcArr.push_back(remoteSatelliteObj);
|
|
|
|
|
2019-05-11 05:01:52 +08:00
|
|
|
if(g_simulator.physicalDatacenters > 5 && deterministicRandom()->random01() < 0.5) {
|
2018-11-05 11:53:55 +08:00
|
|
|
StatusObject primarySatelliteObjB;
|
|
|
|
primarySatelliteObjB["id"] = "4";
|
|
|
|
primarySatelliteObjB["priority"] = 1;
|
|
|
|
primarySatelliteObjB["satellite"] = 1;
|
|
|
|
primaryDcArr.push_back(primarySatelliteObjB);
|
|
|
|
|
|
|
|
StatusObject remoteSatelliteObjB;
|
|
|
|
remoteSatelliteObjB["id"] = "5";
|
|
|
|
remoteSatelliteObjB["priority"] = 1;
|
|
|
|
remoteSatelliteObjB["satellite"] = 1;
|
|
|
|
remoteDcArr.push_back(remoteSatelliteObjB);
|
|
|
|
|
2019-05-11 05:01:52 +08:00
|
|
|
int satellite_replication_type = deterministicRandom()->randomInt(0,3);
|
2018-11-05 11:53:55 +08:00
|
|
|
switch (satellite_replication_type) {
|
|
|
|
case 0: {
|
|
|
|
TEST( true ); // Simulated cluster using no satellite redundancy mode
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1: {
|
|
|
|
TEST( true ); // Simulated cluster using two satellite fast redundancy mode
|
|
|
|
primaryObj["satellite_redundancy_mode"] = "two_satellite_fast";
|
|
|
|
remoteObj["satellite_redundancy_mode"] = "two_satellite_fast";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: {
|
|
|
|
TEST( true ); // Simulated cluster using two satellite safe redundancy mode
|
|
|
|
primaryObj["satellite_redundancy_mode"] = "two_satellite_safe";
|
|
|
|
remoteObj["satellite_redundancy_mode"] = "two_satellite_safe";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
ASSERT(false); // Programmer forgot to adjust cases.
|
|
|
|
}
|
|
|
|
} else {
|
2019-05-11 05:01:52 +08:00
|
|
|
int satellite_replication_type = deterministicRandom()->randomInt(0,4);
|
2018-11-05 11:53:55 +08:00
|
|
|
switch (satellite_replication_type) {
|
|
|
|
case 0: {
|
|
|
|
//FIXME: implement
|
|
|
|
TEST( true ); // Simulated cluster using custom satellite redundancy mode
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1: {
|
|
|
|
TEST( true ); // Simulated cluster using no satellite redundancy mode
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: {
|
|
|
|
TEST( true ); // Simulated cluster using single satellite redundancy mode
|
|
|
|
primaryObj["satellite_redundancy_mode"] = "one_satellite_single";
|
|
|
|
remoteObj["satellite_redundancy_mode"] = "one_satellite_single";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3: {
|
|
|
|
TEST( true ); // Simulated cluster using double satellite redundancy mode
|
|
|
|
primaryObj["satellite_redundancy_mode"] = "one_satellite_double";
|
|
|
|
remoteObj["satellite_redundancy_mode"] = "one_satellite_double";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
ASSERT(false); // Programmer forgot to adjust cases.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-11 05:01:52 +08:00
|
|
|
if (deterministicRandom()->random01() < 0.25) {
|
|
|
|
int logs = deterministicRandom()->randomInt(1,7);
|
2018-11-05 11:53:55 +08:00
|
|
|
primaryObj["satellite_logs"] = logs;
|
|
|
|
remoteObj["satellite_logs"] = logs;
|
|
|
|
}
|
|
|
|
|
2019-05-11 05:01:52 +08:00
|
|
|
int remote_replication_type = deterministicRandom()->randomInt(0, 4);
|
2018-11-05 11:53:55 +08:00
|
|
|
switch (remote_replication_type) {
|
|
|
|
case 0: {
|
|
|
|
//FIXME: implement
|
|
|
|
TEST( true ); // Simulated cluster using custom remote redundancy mode
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1: {
|
|
|
|
TEST( true ); // Simulated cluster using default remote redundancy mode
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: {
|
|
|
|
TEST( true ); // Simulated cluster using single remote redundancy mode
|
|
|
|
result += " remote_single";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3: {
|
|
|
|
TEST( true ); // Simulated cluster using double remote redundancy mode
|
|
|
|
result += " remote_double";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
ASSERT(false); // Programmer forgot to adjust cases.
|
|
|
|
}
|
|
|
|
|
2019-05-11 05:01:52 +08:00
|
|
|
result += format(" log_routers=%d", deterministicRandom()->randomInt(1,7));
|
|
|
|
result += format(" remote_logs=%d", deterministicRandom()->randomInt(1,7));
|
2018-11-05 11:53:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
primaryObj["datacenters"] = primaryDcArr;
|
|
|
|
remoteObj["datacenters"] = remoteDcArr;
|
|
|
|
|
|
|
|
StatusArray regionArr;
|
|
|
|
regionArr.push_back(primaryObj);
|
|
|
|
|
2019-05-11 05:01:52 +08:00
|
|
|
if(deterministicRandom()->random01() < 0.8) {
|
2018-11-05 11:53:55 +08:00
|
|
|
regionArr.push_back(remoteObj);
|
2019-05-11 05:01:52 +08:00
|
|
|
if(deterministicRandom()->random01() < 0.25) {
|
|
|
|
result += format(" usable_regions=%d", deterministicRandom()->randomInt(1,3));
|
2018-11-05 11:53:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result += " regions=" + json_spirit::write_string(json_spirit::mValue(regionArr), json_spirit::Output_options::none);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
struct ConfigureDatabaseWorkload : TestWorkload {
|
|
|
|
double testDuration;
|
|
|
|
int additionalDBs;
|
|
|
|
|
|
|
|
vector<Future<Void>> clients;
|
|
|
|
PerfIntCounter retries;
|
|
|
|
|
|
|
|
ConfigureDatabaseWorkload( WorkloadContext const& wcx )
|
|
|
|
: TestWorkload(wcx), retries("Retries")
|
|
|
|
{
|
|
|
|
testDuration = getOption( options, LiteralStringRef("testDuration"), 200.0 );
|
2018-11-10 02:06:03 +08:00
|
|
|
g_simulator.usableRegions = 1;
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual std::string description() { return "DestroyDatabaseWorkload"; }
|
|
|
|
|
|
|
|
virtual Future<Void> setup( Database const& cx ) {
|
2018-02-15 08:07:23 +08:00
|
|
|
return _setup( cx, this );
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual Future<Void> start( Database const& cx ) {
|
2018-02-15 08:07:23 +08:00
|
|
|
return _start( this, cx );
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
virtual Future<bool> check( Database const& cx ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void getMetrics( vector<PerfMetric>& m ) {
|
|
|
|
m.push_back( retries.getMetric() );
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint64_t valueToUInt64( const StringRef& v ) {
|
|
|
|
long long unsigned int x = 0;
|
|
|
|
sscanf( v.toString().c_str(), "%llx", &x );
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline Standalone<StringRef> getDatabaseName( ConfigureDatabaseWorkload *self, int dbIndex ) {
|
|
|
|
return StringRef(format("DestroyDB%d", dbIndex));
|
|
|
|
}
|
|
|
|
|
2019-03-04 04:57:39 +08:00
|
|
|
static Future<ConfigurationResult::Type> IssueConfigurationChange( Database cx, const std::string& config, bool force ) {
|
|
|
|
printf("Issuing configuration change: %s\n", config.c_str());
|
|
|
|
return changeConfig(cx, config, force);
|
|
|
|
}
|
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
ACTOR Future<Void> _setup( Database cx, ConfigureDatabaseWorkload *self ) {
|
2019-02-13 08:07:17 +08:00
|
|
|
wait(success( changeConfig( cx, "single", true ) ));
|
2017-05-26 04:48:44 +08:00
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR Future<Void> _start( ConfigureDatabaseWorkload *self, Database cx ) {
|
|
|
|
if( self->clientId == 0 ) {
|
|
|
|
self->clients.push_back( timeout( self->singleDB( self, cx ), self->testDuration, Void() ) );
|
2018-08-11 04:57:10 +08:00
|
|
|
wait( waitForAll( self->clients ) );
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int randomRoleNumber() {
|
2019-05-11 05:01:52 +08:00
|
|
|
int i = deterministicRandom()->randomInt(0,4);
|
2017-05-26 04:48:44 +08:00
|
|
|
return i ? i : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR Future<Void> singleDB( ConfigureDatabaseWorkload *self, Database cx ) {
|
|
|
|
state Transaction tr;
|
|
|
|
state int i;
|
|
|
|
loop {
|
|
|
|
if(g_simulator.speedUpSimulation) {
|
|
|
|
return Void();
|
|
|
|
}
|
2019-05-11 05:01:52 +08:00
|
|
|
state int randomChoice = deterministicRandom()->randomInt(0, 7);
|
2017-05-26 04:48:44 +08:00
|
|
|
if( randomChoice == 0 ) {
|
2019-05-11 05:01:52 +08:00
|
|
|
double waitDuration = 3.0 * deterministicRandom()->random01();
|
2017-05-26 04:48:44 +08:00
|
|
|
//TraceEvent("ConfigureTestWaitAfter").detail("WaitDuration",waitDuration);
|
2018-08-11 04:57:10 +08:00
|
|
|
wait( delay( waitDuration ) );
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
else if( randomChoice == 1 ) {
|
|
|
|
tr = Transaction( cx );
|
|
|
|
loop {
|
|
|
|
try {
|
|
|
|
tr.clear( normalKeys );
|
2018-08-11 04:57:10 +08:00
|
|
|
wait( tr.commit() );
|
2017-05-26 04:48:44 +08:00
|
|
|
break;
|
|
|
|
} catch( Error &e ) {
|
2018-08-11 04:57:10 +08:00
|
|
|
wait( tr.onError(e) );
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( randomChoice == 2 ) {
|
2019-05-11 05:01:52 +08:00
|
|
|
state double loadDuration = deterministicRandom()->random01() * 10.0;
|
2017-05-26 04:48:44 +08:00
|
|
|
state double startTime = now();
|
|
|
|
state int amtLoaded = 0;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
if( now() - startTime > loadDuration )
|
|
|
|
break;
|
|
|
|
loop {
|
|
|
|
tr = Transaction( cx );
|
|
|
|
try {
|
|
|
|
for( i = 0; i < 10; i++ ) {
|
2019-05-11 05:01:52 +08:00
|
|
|
state Key randomKey( "ConfigureTest" + deterministicRandom()->randomUniqueID().toString() );
|
2017-05-26 04:48:44 +08:00
|
|
|
Optional<Value> val = wait( tr.get( randomKey ) );
|
|
|
|
uint64_t nextVal = val.present() ? valueToUInt64( val.get() ) + 1 : 0;
|
|
|
|
tr.set( randomKey, format( "%016llx", nextVal ) );
|
|
|
|
}
|
2018-08-11 04:57:10 +08:00
|
|
|
wait( tr.commit() );
|
2017-05-26 04:48:44 +08:00
|
|
|
amtLoaded += 10;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
catch( Error& e ) {
|
2018-08-11 04:57:10 +08:00
|
|
|
wait( tr.onError( e ) );
|
2017-05-26 04:48:44 +08:00
|
|
|
++self->retries;
|
|
|
|
}
|
|
|
|
}
|
2018-08-11 04:57:10 +08:00
|
|
|
wait( delay( 0.1 ) );
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//TraceEvent("ConfigureTestLoadData").detail("LoadTime", now() - startTime).detail("AmountLoaded",amtLoaded);
|
|
|
|
}
|
|
|
|
else if( randomChoice == 3 ) {
|
2018-06-09 02:11:08 +08:00
|
|
|
//TraceEvent("ConfigureTestConfigureBegin").detail("NewConfig", newConfig);
|
2018-11-05 11:53:55 +08:00
|
|
|
int maxRedundancies = sizeof(redundancies)/sizeof(redundancies[0]);
|
|
|
|
if(g_simulator.physicalDatacenters == 2 || g_simulator.physicalDatacenters > 3) {
|
|
|
|
maxRedundancies--; //There are not enough machines for triple replication in fearless configurations
|
2018-07-10 13:01:46 +08:00
|
|
|
}
|
2019-05-11 05:01:52 +08:00
|
|
|
int redundancy = deterministicRandom()->randomInt(0, maxRedundancies);
|
2018-11-05 11:53:55 +08:00
|
|
|
std::string config = redundancies[redundancy];
|
|
|
|
|
2018-02-20 08:49:57 +08:00
|
|
|
if(config == "triple" && g_simulator.physicalDatacenters == 3) {
|
2018-11-05 11:53:55 +08:00
|
|
|
config = "three_data_hall ";
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
2018-11-13 09:40:40 +08:00
|
|
|
config += generateRegions();
|
2018-11-05 11:53:55 +08:00
|
|
|
|
2019-05-11 05:01:52 +08:00
|
|
|
if (deterministicRandom()->random01() < 0.5) config += " logs=" + format("%d", randomRoleNumber());
|
|
|
|
if (deterministicRandom()->random01() < 0.5) config += " proxies=" + format("%d", randomRoleNumber());
|
|
|
|
if (deterministicRandom()->random01() < 0.5) config += " resolvers=" + format("%d", randomRoleNumber());
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2019-03-04 04:57:39 +08:00
|
|
|
wait(success( IssueConfigurationChange( cx, config, false ) ));
|
2018-11-05 11:53:55 +08:00
|
|
|
|
2018-06-09 02:11:08 +08:00
|
|
|
//TraceEvent("ConfigureTestConfigureEnd").detail("NewConfig", newConfig);
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
else if( randomChoice == 4 ) {
|
2018-06-09 02:11:08 +08:00
|
|
|
//TraceEvent("ConfigureTestQuorumBegin").detail("NewQuorum", s);
|
2017-05-26 04:48:44 +08:00
|
|
|
auto ch = autoQuorumChange();
|
2019-05-11 05:01:52 +08:00
|
|
|
if (deterministicRandom()->randomInt(0,2))
|
|
|
|
ch = nameQuorumChange( format("NewName%d", deterministicRandom()->randomInt(0,100)), ch );
|
2019-02-13 08:07:17 +08:00
|
|
|
wait(success( changeQuorum( cx, ch ) ));
|
2018-06-09 02:11:08 +08:00
|
|
|
//TraceEvent("ConfigureTestConfigureEnd").detail("NewQuorum", s);
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
else if ( randomChoice == 5) {
|
2019-05-11 05:01:52 +08:00
|
|
|
wait(success( IssueConfigurationChange( cx, storeTypes[deterministicRandom()->randomInt( 0, sizeof(storeTypes)/sizeof(storeTypes[0]))], true ) ));
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
2019-02-08 09:02:45 +08:00
|
|
|
else if ( randomChoice == 6 ) {
|
2019-02-23 04:15:23 +08:00
|
|
|
// Some configurations will be invalid, and that's fine.
|
2019-05-11 05:01:52 +08:00
|
|
|
wait(success( IssueConfigurationChange( cx, logTypes[deterministicRandom()->randomInt( 0, sizeof(logTypes)/sizeof(logTypes[0]))], false ) ));
|
2019-02-08 09:02:45 +08:00
|
|
|
}
|
2017-05-26 04:48:44 +08:00
|
|
|
else {
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
WorkloadFactory<ConfigureDatabaseWorkload> DestroyDatabaseWorkloadFactory("ConfigureDatabase");
|