1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2014 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 8 package com.sleepycat.je.rep; 9 10 import static org.junit.Assert.assertFalse; 11 import static org.junit.Assert.assertTrue; 12 13 import java.io.File; 14 15 import org.junit.Before; 16 import org.junit.Test; 17 18 import com.sleepycat.je.Database; 19 import com.sleepycat.je.DatabaseConfig; 20 import com.sleepycat.je.DatabaseExistsException; 21 import com.sleepycat.je.EnvironmentConfig; 22 import com.sleepycat.je.rep.utilint.RepTestUtils; 23 import com.sleepycat.je.rep.utilint.RepTestUtils.RepEnvInfo; 24 import com.sleepycat.util.test.SharedTestUtils; 25 import com.sleepycat.util.test.TestBase; 26 27 /** 28 * Check that both master and replica nodes catch invalid environment and 29 * database configurations. 30 */ 31 public class CheckConfigTest extends TestBase { 32 33 private File envRoot; 34 private File[] envHomes; 35 36 @Before setUp()37 public void setUp() 38 throws Exception { 39 40 envRoot = SharedTestUtils.getTestDir(); 41 envHomes = RepTestUtils.makeRepEnvDirs(envRoot, 2); 42 super.setUp(); 43 } 44 45 /** 46 * Replicated environments do not support non transactional mode. 47 */ 48 @Test testEnvNonTransactionalConfig()49 public void testEnvNonTransactionalConfig() 50 throws Exception { 51 52 EnvironmentConfig config = createConfig(); 53 config.setTransactional(false); 54 expectRejection(config); 55 } 56 57 /** 58 * A configuration of transactional + noLocking is invalid. 59 */ 60 @Test testEnvNoLockingConfig()61 public void testEnvNoLockingConfig() 62 throws Exception { 63 64 EnvironmentConfig config = createConfig(); 65 config.setLocking(false); 66 expectRejection(config); 67 } 68 69 /** 70 * ReadOnly = true should be accepted. 71 * 72 * Since setting environment read only is only possible when an Environment 73 * exists, this test first creates a normal Environment and then reopens it 74 * with read only configuration. 75 */ 76 @Test testEnvReadOnlyConfig()77 public void testEnvReadOnlyConfig() 78 throws Exception { 79 80 EnvironmentConfig config = createConfig(); 81 expectAcceptance(config); 82 config.setReadOnly(true); 83 expectRejection(config); 84 } 85 86 /** 87 * AllowCreate = false should be accepted. 88 * 89 * Since setting environment allowCreate to false is only possible when an 90 * Environment exists, this test creates a normal Environment and then 91 * reopens it with allowCreate=false configuration. 92 */ 93 @Test testEnvAllowCreateFalseConfig()94 public void testEnvAllowCreateFalseConfig() 95 throws Exception { 96 97 EnvironmentConfig config = createConfig(); 98 expectAcceptance(config); 99 config.setAllowCreate(false); 100 expectAcceptance(config); 101 } 102 103 /** 104 * SharedCache = true should be accepted. 105 */ 106 @Test testEnvSharedCacheConfig()107 public void testEnvSharedCacheConfig() 108 throws Exception { 109 110 EnvironmentConfig config = createConfig(); 111 config.setSharedCache(true); 112 expectAcceptance(config); 113 } 114 115 /** 116 * Serializable isolation = true should be accepted. 117 */ 118 @Test testEnvSerializableConfig()119 public void testEnvSerializableConfig() 120 throws Exception { 121 122 EnvironmentConfig config = createConfig(); 123 config.setTxnSerializableIsolation(true); 124 expectAcceptance(config); 125 } 126 127 /** 128 * Return a new transactional EnvironmentConfig for test use. 129 */ createConfig()130 private EnvironmentConfig createConfig() { 131 EnvironmentConfig config = new EnvironmentConfig(); 132 config.setAllowCreate(true); 133 config.setTransactional(true); 134 135 return config; 136 } 137 138 /** 139 * Return a new transactional DatabaseConfig for test use. 140 */ createDbConfig()141 private DatabaseConfig createDbConfig() { 142 DatabaseConfig config = new DatabaseConfig(); 143 config.setAllowCreate(true); 144 config.setTransactional(true); 145 146 return config; 147 } 148 149 /** 150 * Wrap checkEnvConfig in this method to make the intent of the test 151 * obvious. 152 */ expectAcceptance(EnvironmentConfig envConfig)153 private void expectAcceptance(EnvironmentConfig envConfig) 154 throws Exception { 155 156 checkEnvConfig(envConfig, false /* isInvalid */); 157 } 158 159 /** 160 * Wrap checkEnvConfig in this method to make the intent of the test 161 * obvious. 162 */ expectRejection(EnvironmentConfig envConfig)163 private void expectRejection(EnvironmentConfig envConfig) 164 throws Exception { 165 166 checkEnvConfig(envConfig, true /* isInvalid */); 167 } 168 169 /** 170 * Check whether an EnvironmentConfig is valid. 171 * 172 * @param envConfig The EnvironmentConfig we'd like to check. 173 * @param isInvalid if true, envConfig represents an invalid configuration 174 * and we expect ReplicatedEnvironment creation to fail. 175 */ checkEnvConfig(EnvironmentConfig envConfig, boolean isInvalid)176 private void checkEnvConfig(EnvironmentConfig envConfig, 177 boolean isInvalid) 178 throws Exception { 179 180 /* 181 * masterFail and replicaFail are true if the master or replica 182 * environment creation failed. 183 */ 184 boolean masterFail = false; 185 boolean replicaFail = false; 186 187 ReplicatedEnvironment master = null; 188 ReplicatedEnvironment replica = null; 189 190 /* Create the ReplicationConfig for master and replica. */ 191 ReplicationConfig masterConfig = RepTestUtils.createRepConfig(1); 192 masterConfig.setDesignatedPrimary(true); 193 194 masterConfig.setHelperHosts(masterConfig.getNodeHostPort()); 195 ReplicationConfig replicaConfig = RepTestUtils.createRepConfig(2); 196 replicaConfig.setHelperHosts(masterConfig.getNodeHostPort()); 197 198 /* 199 * Attempt to create the master with the specified EnvironmentConfig. 200 */ 201 try { 202 master = new ReplicatedEnvironment(envHomes[0], 203 masterConfig, 204 envConfig); 205 } catch (IllegalArgumentException e) { 206 masterFail = true; 207 } 208 209 /* 210 * If the specified EnvironmentConfig is expected to fail, the above 211 * master creation fails, so when the test tries to create a replica 212 * in the following steps, it actually tries to create a master. 213 * 214 * Since the test needs to test on both master and replica, so create 215 * a real master here. 216 */ 217 if (isInvalid) { 218 EnvironmentConfig okConfig = 219 RepTestUtils.createEnvConfig(RepTestUtils.DEFAULT_DURABILITY); 220 master = new ReplicatedEnvironment(envHomes[0], masterConfig, 221 okConfig); 222 } 223 224 /* Check the specified EnvironmentConfig on the replica. */ 225 try { 226 replica = new ReplicatedEnvironment(envHomes[1], 227 replicaConfig, 228 envConfig); 229 } catch (IllegalArgumentException e) { 230 replicaFail = true; 231 } 232 233 /* Check whether the master and replica creations are as expected. */ 234 if (isInvalid) { 235 assertTrue(replicaFail && masterFail); 236 } else { 237 assertFalse(replicaFail || masterFail); 238 } 239 240 /* 241 * If the specified EnvironmentConfig is expected to fail, close 242 * the master and return. 243 */ 244 if (isInvalid) { 245 if (master != null) { 246 assertTrue(master.getState().isMaster()); 247 master.close(); 248 } 249 250 return; 251 } 252 253 if (master != null && replica != null) { 254 /* 255 * If the specified EnvironmentConfig is correct, wait for 256 * replication initialization to finish. 257 */ 258 while (replica.getState() != ReplicatedEnvironment.State.REPLICA) { 259 Thread.sleep(1000); 260 } 261 262 /* Make sure the test runs on both master and replica. */ 263 assertTrue(master.getState().isMaster()); 264 assertTrue(!replica.getState().isMaster()); 265 266 /* Close the replica and master. */ 267 replica.close(); 268 master.close(); 269 } 270 } 271 272 /** 273 * AllowCreate = false should be accepted. 274 * 275 * Setting allowCreate to false is only possible when the database already 276 * exists. Because of that, this test first creates a database and then 277 * reopens it with allowCreate = false configuration. 278 */ 279 @Test testDbAllowCreateFalseConfig()280 public void testDbAllowCreateFalseConfig() 281 throws Exception { 282 283 DatabaseConfig dbConfig = createDbConfig(); 284 expectDbAcceptance(dbConfig, true); 285 dbConfig.setAllowCreate(false); 286 expectDbAcceptance(dbConfig, false); 287 } 288 289 /** 290 * Replicated datatabases do not support non transactional mode. 291 */ 292 @Test testDbNonTransactionalConfig()293 public void testDbNonTransactionalConfig() 294 throws Exception { 295 296 DatabaseConfig dbConfig = createDbConfig(); 297 dbConfig.setTransactional(false); 298 expectDbRejection(dbConfig, false); 299 } 300 301 /** 302 * A database configuration of transactional + deferredWrite is invalid. 303 */ 304 @Test testDbDeferredWriteConfig()305 public void testDbDeferredWriteConfig() 306 throws Exception { 307 308 DatabaseConfig dbConfig = createDbConfig(); 309 dbConfig.setDeferredWrite(true); 310 expectDbRejection(dbConfig, false); 311 } 312 313 /** 314 * A database configuration of transactional + temporary is invalid. 315 */ 316 @Test testDbTemporaryConfig()317 public void testDbTemporaryConfig() 318 throws Exception { 319 320 DatabaseConfig dbConfig = createDbConfig(); 321 dbConfig.setTemporary(true); 322 expectDbRejection(dbConfig, false); 323 } 324 325 /** 326 * ExclusiveCreate = true should be accepted on the master. 327 * 328 * Setting exclusiveCreate is expected to fail on the replica. It's because 329 * when a database is created on master, replication will create the same 330 * database on the replica. When the replica tries to create the database, 331 * it will find the database already exists. When we set exclusiveCreate = 332 * true, the replica will throw out a DatabaseExistException. The check 333 * for this is done within the logic for expectDbAcceptance. 334 */ 335 @Test testDbExclusiveCreateConfig()336 public void testDbExclusiveCreateConfig() 337 throws Exception { 338 339 DatabaseConfig dbConfig = createDbConfig(); 340 dbConfig.setExclusiveCreate(true); 341 expectDbAcceptance(dbConfig, true); 342 } 343 344 /** 345 * KeyPrefixing = true should be accpted. 346 */ 347 @Test testDbKeyPrefixingConfig()348 public void testDbKeyPrefixingConfig() 349 throws Exception { 350 351 DatabaseConfig dbConfig = createDbConfig(); 352 dbConfig.setKeyPrefixing(true); 353 expectDbAcceptance(dbConfig, false); 354 } 355 356 /** 357 * ReadOnly = true should be accpted. 358 * 359 * Database read only is only possible when the database exists, so this 360 * test first creates a database and then reopens it with read only 361 * configuration. 362 */ 363 @Test testDbReadOnlyConfig()364 public void testDbReadOnlyConfig() 365 throws Exception { 366 367 DatabaseConfig dbConfig = createDbConfig(); 368 expectDbAcceptance(dbConfig, true); 369 dbConfig.setReadOnly(true); 370 expectDbAcceptance(dbConfig, false); 371 } 372 373 /** 374 * SortedDuplicates = true should be accpted. 375 */ 376 @Test testDbSortedDuplicatesConfig()377 public void testDbSortedDuplicatesConfig() 378 throws Exception { 379 380 DatabaseConfig dbConfig = createDbConfig(); 381 dbConfig.setSortedDuplicates(true); 382 expectDbAcceptance(dbConfig, false); 383 } 384 385 /** 386 * OverrideBtreeComparator = true should be accepted. 387 */ 388 @Test testDbOverideBtreeComparatorConfig()389 public void testDbOverideBtreeComparatorConfig() 390 throws Exception { 391 392 DatabaseConfig dbConfig = createDbConfig(); 393 dbConfig.setOverrideBtreeComparator(true); 394 expectDbAcceptance(dbConfig, false); 395 } 396 397 /** 398 * OverrideDuplicatComparator = true should be accepted. 399 */ 400 @Test testDbOverrideDuplicateComparatorConfig()401 public void testDbOverrideDuplicateComparatorConfig() 402 throws Exception { 403 404 DatabaseConfig dbConfig = createDbConfig(); 405 dbConfig.setOverrideDuplicateComparator(true); 406 expectDbAcceptance(dbConfig, false); 407 } 408 409 /** 410 * UseExistingConfig = true should be accepted. 411 * 412 * UseExistingConfig is only possible when the database exists, so this 413 * test first creates a database and then reopens it with UseExistingConfig 414 * configuration. 415 */ 416 @Test testDbUseExistingConfig()417 public void testDbUseExistingConfig() 418 throws Exception { 419 420 DatabaseConfig dbConfig = createDbConfig(); 421 expectDbAcceptance(dbConfig, true); 422 dbConfig = new DatabaseConfig(); 423 dbConfig.setTransactional(true); 424 dbConfig.setUseExistingConfig(true); 425 dbConfig.setReadOnly(true); 426 expectDbAcceptance(dbConfig, false); 427 } 428 429 /** 430 * Wrap checkDbConfig in this method to make the intent of the test 431 * obvious. 432 */ expectDbAcceptance(DatabaseConfig dbConfig, boolean doSync)433 private void expectDbAcceptance(DatabaseConfig dbConfig, boolean doSync) 434 throws Exception { 435 436 checkDbConfig(dbConfig, false /* isInvalid */, doSync); 437 } 438 439 /** 440 * Wrap checkEnvConfig in this method to make the intent of the test 441 * obvious. 442 */ expectDbRejection(DatabaseConfig dbConfig, boolean doSync)443 private void expectDbRejection(DatabaseConfig dbConfig, boolean doSync) 444 throws Exception { 445 446 checkDbConfig(dbConfig, true /* isInvalid */, doSync); 447 } 448 449 /** 450 * The main function checks whether a database configuration is valid. 451 * 452 * @param dbConfig The DatabaseConfig to check. 453 * @param isInvalid if true, dbConfig represents an invalid configuration 454 * and we expect database creation to fail. 455 * @param doSync If true, the test should do a group sync after creating 456 * the database on the master 457 */ checkDbConfig(DatabaseConfig dbConfig, boolean isInvalid, boolean doSync)458 public void checkDbConfig(DatabaseConfig dbConfig, 459 boolean isInvalid, 460 boolean doSync) 461 throws Exception { 462 463 /* 464 * masterFail and replicaFail are true if the master or replica 465 * database creation failed. 466 */ 467 boolean masterFail = false; 468 boolean replicaFail =false; 469 470 /* Create an array of replicators successfully and join the group. */ 471 RepEnvInfo[] repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 2); 472 repEnvInfo[0].getRepConfig().setDesignatedPrimary(true); 473 RepTestUtils.joinGroup(repEnvInfo); 474 475 /* Create the database with the specified configuration on master. */ 476 Database masterDb = null; 477 try { 478 masterDb = repEnvInfo[0].getEnv().openDatabase(null, "test", 479 dbConfig); 480 } catch (IllegalArgumentException e) { 481 masterFail = true; 482 } 483 484 /* 485 * The test does a group sync when the tested configuration needs to 486 * create a real database first. 487 * 488 * If a group sync isn't done, the replica would incorrectly try to 489 * create the database since it hasn't seen it yet. Since write 490 * operations on the replica are forbidden, the test would fail, which 491 * is not expected. 492 */ 493 if (doSync) { 494 RepTestUtils.syncGroupToLastCommit(repEnvInfo, repEnvInfo.length); 495 } 496 497 /* Open the database with the specified configuration on replica. */ 498 Database replicaDb = null; 499 try { 500 replicaDb = repEnvInfo[1].getEnv().openDatabase(null, "test", 501 dbConfig); 502 } catch (IllegalArgumentException e) { 503 replicaFail = true; 504 } catch (ReplicaWriteException e) { 505 /* 506 * If the test throws a ReplicaStateException, it's because it 507 * tries to create a new database on replica, but replica doesn't 508 * allow create operation, it's thought to be valid. 509 */ 510 } catch (DatabaseExistsException e) { 511 replicaFail = true; 512 } 513 514 /* Check the validity here. */ 515 if (isInvalid) { 516 assertTrue(masterFail && replicaFail); 517 } else { 518 519 /* 520 * The exclusiveCreate config is checked explicitly here, because 521 * it has different master/replica behavior. 522 */ 523 if (dbConfig.getExclusiveCreate()) { 524 assertFalse(masterFail); 525 assertTrue(replicaFail); 526 } else { 527 assertFalse(masterFail || replicaFail); 528 } 529 } 530 531 /* Shutdown the databases and environments. */ 532 if (masterDb != null) { 533 masterDb.close(); 534 } 535 536 if (replicaDb != null) { 537 replicaDb.close(); 538 } 539 540 RepTestUtils.shutdownRepEnvs(repEnvInfo); 541 } 542 } 543