1 /*
2 * ConfigureDatabase.actor.cpp
3 *
4 * This source file is part of the FoundationDB open source project
5 *
6 * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 #include "fdbclient/NativeAPI.actor.h"
22 #include "fdbserver/TesterInterface.actor.h"
23 #include "fdbclient/ManagementAPI.actor.h"
24 #include "fdbserver/workloads/workloads.actor.h"
25 #include "fdbrpc/simulator.h"
26 #include "flow/actorcompiler.h" // This must be the last #include.
27
28 // "ssd" is an alias to the preferred type which skews the random distribution toward it but that's okay.
29 static const char* storeTypes[] = { "ssd", "ssd-1", "ssd-2", "memory", "memory-1", "memory-2" };
30 static const char* logTypes[] = { "log_engine:=1", "log_engine:=2", "log_spill:=1", "log_spill:=2", "log_version:=2", "log_version:=3" };
31 static const char* redundancies[] = { "single", "double", "triple" };
32
generateRegions()33 std::string generateRegions() {
34 std::string result;
35 if(g_simulator.physicalDatacenters == 1 || (g_simulator.physicalDatacenters == 2 && g_random->random01() < 0.25) || g_simulator.physicalDatacenters == 3) {
36 return " usable_regions=1 regions=\"\"";
37 }
38
39 if(g_random->random01() < 0.25) {
40 return format(" usable_regions=%d", g_random->randomInt(1,3));
41 }
42
43 int primaryPriority = 1;
44 int remotePriority = -1;
45 double priorityType = g_random->random01();
46 if(priorityType < 0.1) {
47 primaryPriority = -1;
48 remotePriority = 1;
49 } else if(priorityType < 0.2) {
50 remotePriority = 1;
51 primaryPriority = 1;
52 }
53
54 StatusObject primaryObj;
55 StatusObject primaryDcObj;
56 primaryDcObj["id"] = "0";
57 primaryDcObj["priority"] = primaryPriority;
58 StatusArray primaryDcArr;
59 primaryDcArr.push_back(primaryDcObj);
60
61 StatusObject remoteObj;
62 StatusObject remoteDcObj;
63 remoteDcObj["id"] = "1";
64 remoteDcObj["priority"] = remotePriority;
65 StatusArray remoteDcArr;
66 remoteDcArr.push_back(remoteDcObj);
67
68 if(g_simulator.physicalDatacenters > 3 && g_random->random01() < 0.5) {
69 StatusObject primarySatelliteObj;
70 primarySatelliteObj["id"] = "2";
71 primarySatelliteObj["priority"] = 1;
72 primarySatelliteObj["satellite"] = 1;
73 primaryDcArr.push_back(primarySatelliteObj);
74
75 StatusObject remoteSatelliteObj;
76 remoteSatelliteObj["id"] = "3";
77 remoteSatelliteObj["priority"] = 1;
78 remoteSatelliteObj["satellite"] = 1;
79 remoteDcArr.push_back(remoteSatelliteObj);
80
81 if(g_simulator.physicalDatacenters > 5 && g_random->random01() < 0.5) {
82 StatusObject primarySatelliteObjB;
83 primarySatelliteObjB["id"] = "4";
84 primarySatelliteObjB["priority"] = 1;
85 primarySatelliteObjB["satellite"] = 1;
86 primaryDcArr.push_back(primarySatelliteObjB);
87
88 StatusObject remoteSatelliteObjB;
89 remoteSatelliteObjB["id"] = "5";
90 remoteSatelliteObjB["priority"] = 1;
91 remoteSatelliteObjB["satellite"] = 1;
92 remoteDcArr.push_back(remoteSatelliteObjB);
93
94 int satellite_replication_type = g_random->randomInt(0,3);
95 switch (satellite_replication_type) {
96 case 0: {
97 TEST( true ); // Simulated cluster using no satellite redundancy mode
98 break;
99 }
100 case 1: {
101 TEST( true ); // Simulated cluster using two satellite fast redundancy mode
102 primaryObj["satellite_redundancy_mode"] = "two_satellite_fast";
103 remoteObj["satellite_redundancy_mode"] = "two_satellite_fast";
104 break;
105 }
106 case 2: {
107 TEST( true ); // Simulated cluster using two satellite safe redundancy mode
108 primaryObj["satellite_redundancy_mode"] = "two_satellite_safe";
109 remoteObj["satellite_redundancy_mode"] = "two_satellite_safe";
110 break;
111 }
112 default:
113 ASSERT(false); // Programmer forgot to adjust cases.
114 }
115 } else {
116 int satellite_replication_type = g_random->randomInt(0,4);
117 switch (satellite_replication_type) {
118 case 0: {
119 //FIXME: implement
120 TEST( true ); // Simulated cluster using custom satellite redundancy mode
121 break;
122 }
123 case 1: {
124 TEST( true ); // Simulated cluster using no satellite redundancy mode
125 break;
126 }
127 case 2: {
128 TEST( true ); // Simulated cluster using single satellite redundancy mode
129 primaryObj["satellite_redundancy_mode"] = "one_satellite_single";
130 remoteObj["satellite_redundancy_mode"] = "one_satellite_single";
131 break;
132 }
133 case 3: {
134 TEST( true ); // Simulated cluster using double satellite redundancy mode
135 primaryObj["satellite_redundancy_mode"] = "one_satellite_double";
136 remoteObj["satellite_redundancy_mode"] = "one_satellite_double";
137 break;
138 }
139 default:
140 ASSERT(false); // Programmer forgot to adjust cases.
141 }
142 }
143
144 if (g_random->random01() < 0.25) {
145 int logs = g_random->randomInt(1,7);
146 primaryObj["satellite_logs"] = logs;
147 remoteObj["satellite_logs"] = logs;
148 }
149
150 int remote_replication_type = g_random->randomInt(0, 4);
151 switch (remote_replication_type) {
152 case 0: {
153 //FIXME: implement
154 TEST( true ); // Simulated cluster using custom remote redundancy mode
155 break;
156 }
157 case 1: {
158 TEST( true ); // Simulated cluster using default remote redundancy mode
159 break;
160 }
161 case 2: {
162 TEST( true ); // Simulated cluster using single remote redundancy mode
163 result += " remote_single";
164 break;
165 }
166 case 3: {
167 TEST( true ); // Simulated cluster using double remote redundancy mode
168 result += " remote_double";
169 break;
170 }
171 default:
172 ASSERT(false); // Programmer forgot to adjust cases.
173 }
174
175 result += format(" log_routers=%d", g_random->randomInt(1,7));
176 result += format(" remote_logs=%d", g_random->randomInt(1,7));
177 }
178
179 primaryObj["datacenters"] = primaryDcArr;
180 remoteObj["datacenters"] = remoteDcArr;
181
182 StatusArray regionArr;
183 regionArr.push_back(primaryObj);
184
185 if(g_random->random01() < 0.8) {
186 regionArr.push_back(remoteObj);
187 if(g_random->random01() < 0.25) {
188 result += format(" usable_regions=%d", g_random->randomInt(1,3));
189 }
190 }
191
192 result += " regions=" + json_spirit::write_string(json_spirit::mValue(regionArr), json_spirit::Output_options::none);
193 return result;
194 }
195
196
197
198 struct ConfigureDatabaseWorkload : TestWorkload {
199 double testDuration;
200 int additionalDBs;
201
202 vector<Future<Void>> clients;
203 PerfIntCounter retries;
204
ConfigureDatabaseWorkloadConfigureDatabaseWorkload205 ConfigureDatabaseWorkload( WorkloadContext const& wcx )
206 : TestWorkload(wcx), retries("Retries")
207 {
208 testDuration = getOption( options, LiteralStringRef("testDuration"), 200.0 );
209 g_simulator.usableRegions = 1;
210 }
211
descriptionConfigureDatabaseWorkload212 virtual std::string description() { return "DestroyDatabaseWorkload"; }
213
setupConfigureDatabaseWorkload214 virtual Future<Void> setup( Database const& cx ) {
215 return _setup( cx, this );
216 }
217
startConfigureDatabaseWorkload218 virtual Future<Void> start( Database const& cx ) {
219 return _start( this, cx );
220 }
checkConfigureDatabaseWorkload221 virtual Future<bool> check( Database const& cx ) {
222 return true;
223 }
224
getMetricsConfigureDatabaseWorkload225 virtual void getMetrics( vector<PerfMetric>& m ) {
226 m.push_back( retries.getMetric() );
227 }
228
valueToUInt64ConfigureDatabaseWorkload229 static inline uint64_t valueToUInt64( const StringRef& v ) {
230 long long unsigned int x = 0;
231 sscanf( v.toString().c_str(), "%llx", &x );
232 return x;
233 }
234
getDatabaseNameConfigureDatabaseWorkload235 static inline Standalone<StringRef> getDatabaseName( ConfigureDatabaseWorkload *self, int dbIndex ) {
236 return StringRef(format("DestroyDB%d", dbIndex));
237 }
238
IssueConfigurationChangeConfigureDatabaseWorkload239 static Future<ConfigurationResult::Type> IssueConfigurationChange( Database cx, const std::string& config, bool force ) {
240 printf("Issuing configuration change: %s\n", config.c_str());
241 return changeConfig(cx, config, force);
242 }
243
_setupConfigureDatabaseWorkload244 ACTOR Future<Void> _setup( Database cx, ConfigureDatabaseWorkload *self ) {
245 wait(success( changeConfig( cx, "single", true ) ));
246 return Void();
247 }
248
_startConfigureDatabaseWorkload249 ACTOR Future<Void> _start( ConfigureDatabaseWorkload *self, Database cx ) {
250 if( self->clientId == 0 ) {
251 self->clients.push_back( timeout( self->singleDB( self, cx ), self->testDuration, Void() ) );
252 wait( waitForAll( self->clients ) );
253 }
254 return Void();
255 }
256
randomRoleNumberConfigureDatabaseWorkload257 static int randomRoleNumber() {
258 int i = g_random->randomInt(0,4);
259 return i ? i : -1;
260 }
261
singleDBConfigureDatabaseWorkload262 ACTOR Future<Void> singleDB( ConfigureDatabaseWorkload *self, Database cx ) {
263 state Transaction tr;
264 state int i;
265 state bool firstFearless = false;
266 loop {
267 if(g_simulator.speedUpSimulation) {
268 return Void();
269 }
270 state int randomChoice = g_random->randomInt(0, 7);
271 if( randomChoice == 0 ) {
272 double waitDuration = 3.0 * g_random->random01();
273 //TraceEvent("ConfigureTestWaitAfter").detail("WaitDuration",waitDuration);
274 wait( delay( waitDuration ) );
275 }
276 else if( randomChoice == 1 ) {
277 tr = Transaction( cx );
278 loop {
279 try {
280 tr.clear( normalKeys );
281 wait( tr.commit() );
282 break;
283 } catch( Error &e ) {
284 wait( tr.onError(e) );
285 }
286 }
287 }
288 else if( randomChoice == 2 ) {
289 state double loadDuration = g_random->random01() * 10.0;
290 state double startTime = now();
291 state int amtLoaded = 0;
292
293 loop {
294 if( now() - startTime > loadDuration )
295 break;
296 loop {
297 tr = Transaction( cx );
298 try {
299 for( i = 0; i < 10; i++ ) {
300 state Key randomKey( "ConfigureTest" + g_random->randomUniqueID().toString() );
301 Optional<Value> val = wait( tr.get( randomKey ) );
302 uint64_t nextVal = val.present() ? valueToUInt64( val.get() ) + 1 : 0;
303 tr.set( randomKey, format( "%016llx", nextVal ) );
304 }
305 wait( tr.commit() );
306 amtLoaded += 10;
307 break;
308 }
309 catch( Error& e ) {
310 wait( tr.onError( e ) );
311 ++self->retries;
312 }
313 }
314 wait( delay( 0.1 ) );
315 }
316
317 //TraceEvent("ConfigureTestLoadData").detail("LoadTime", now() - startTime).detail("AmountLoaded",amtLoaded);
318 }
319 else if( randomChoice == 3 ) {
320 //TraceEvent("ConfigureTestConfigureBegin").detail("NewConfig", newConfig);
321 int maxRedundancies = sizeof(redundancies)/sizeof(redundancies[0]);
322 if(g_simulator.physicalDatacenters == 2 || g_simulator.physicalDatacenters > 3) {
323 maxRedundancies--; //There are not enough machines for triple replication in fearless configurations
324 }
325 int redundancy = g_random->randomInt(0, maxRedundancies);
326 std::string config = redundancies[redundancy];
327
328 if(config == "triple" && g_simulator.physicalDatacenters == 3) {
329 config = "three_data_hall ";
330 }
331
332 config += generateRegions();
333
334 if (g_random->random01() < 0.5) config += " logs=" + format("%d", randomRoleNumber());
335 if (g_random->random01() < 0.5) config += " proxies=" + format("%d", randomRoleNumber());
336 if (g_random->random01() < 0.5) config += " resolvers=" + format("%d", randomRoleNumber());
337
338 wait(success( IssueConfigurationChange( cx, config, false ) ));
339
340 //TraceEvent("ConfigureTestConfigureEnd").detail("NewConfig", newConfig);
341 }
342 else if( randomChoice == 4 ) {
343 //TraceEvent("ConfigureTestQuorumBegin").detail("NewQuorum", s);
344 auto ch = autoQuorumChange();
345 if (g_random->randomInt(0,2))
346 ch = nameQuorumChange( format("NewName%d", g_random->randomInt(0,100)), ch );
347 wait(success( changeQuorum( cx, ch ) ));
348 //TraceEvent("ConfigureTestConfigureEnd").detail("NewQuorum", s);
349 }
350 else if ( randomChoice == 5) {
351 wait(success( IssueConfigurationChange( cx, storeTypes[g_random->randomInt( 0, sizeof(storeTypes)/sizeof(storeTypes[0]))], true ) ));
352 }
353 else if ( randomChoice == 6 ) {
354 // Some configurations will be invalid, and that's fine.
355 wait(success( IssueConfigurationChange( cx, logTypes[g_random->randomInt( 0, sizeof(logTypes)/sizeof(logTypes[0]))], false ) ));
356 }
357 else {
358 ASSERT(false);
359 }
360 }
361 }
362 };
363
364 WorkloadFactory<ConfigureDatabaseWorkload> DestroyDatabaseWorkloadFactory("ConfigureDatabase");
365