/* * ToroDB * Copyright © 2014 8Kdata Technology (www.8kdata.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.torodb.mongodb.repl.oplogreplier; import com.eightkdata.mongowp.annotations.MongoWp; import com.eightkdata.mongowp.server.MongoServerConfig; import com.google.common.net.HostAndPort; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.inject.*; import com.torodb.backend.derby.guice.DerbyBackendModule; import com.torodb.backend.driver.derby.DerbyDbBackendConfiguration; import com.torodb.backend.guice.BackendModule; import com.torodb.concurrent.DefaultConcurrentToolsFactory; import com.torodb.concurrent.DefaultConcurrentToolsFactory.BlockerThreadFactoryFunction; import com.torodb.concurrent.DefaultConcurrentToolsFactory.ForkJoinThreadFactoryFunction; import com.torodb.concurrent.guice.ConcurrentModule; import com.torodb.core.BuildProperties; import com.torodb.core.annotations.ParallelLevel; import com.torodb.core.annotations.TorodbIdleService; import com.torodb.core.annotations.TorodbRunnableService; import com.torodb.core.backend.BackendBundle; import com.torodb.core.backend.BackendBundleFactory; import com.torodb.core.guice.CoreModule; import com.torodb.core.metrics.MetricsConfig; import com.torodb.core.metrics.guice.MetricsModule; import com.torodb.core.supervision.Supervisor; import com.torodb.core.supervision.SupervisorDecision; import com.torodb.d2r.guice.D2RModule; import com.torodb.metainfo.guice.MetainfModule; import com.torodb.mongodb.core.MongodServer; import com.torodb.mongodb.core.MongodServerConfig; import com.torodb.mongodb.guice.MongoLayerModule; import com.torodb.mongodb.repl.OplogManager; import com.torodb.mongodb.repl.commands.ReplCommandsGuiceModule; import com.torodb.mongodb.repl.guice.AkkaDbClonerProvider; import com.torodb.mongodb.repl.guice.DocsPerTransaction; import com.torodb.mongodb.repl.guice.MongoDbRepl; import com.torodb.mongodb.repl.guice.MongoDbReplModule.DefaultCommitHeuristic; import com.torodb.mongodb.repl.oplogreplier.DefaultOplogApplier.BatchLimits; import com.torodb.mongodb.repl.oplogreplier.batch.AnalyzedOplogBatchExecutor; import com.torodb.mongodb.utils.DbCloner; import com.torodb.mongodb.utils.cloner.CommitHeuristic; import com.torodb.torod.TorodBundle; import com.torodb.torod.TorodBundleFactory; import com.torodb.torod.TorodServer; import com.torodb.torod.guice.MemoryTorodModule; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.rules.ExternalResource; import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.time.format.DateTimeParseException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory; import java.util.concurrent.ForkJoinWorkerThread; import java.util.concurrent.ThreadFactory; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * */ public class OplogTestContextResourceRule extends ExternalResource { private static final Logger LOGGER = LogManager.getLogger(OplogTestContextResourceRule.class); private final Supplier<Module> specificModuleSupplier; private Injector testInjector; private MongodServer mongodServer; private OplogApplier oplogApplier; private TorodServer torodServer; private OplogManager oplogManager; private OplogTestContext testContext; private AnalyzedOplogBatchExecutor aobe; public OplogTestContextResourceRule(Supplier<Module> specificModuleSupplier) { this.specificModuleSupplier = specificModuleSupplier; } public OplogTestContext getTestContext() { return testContext; } @Override protected void before() throws Throwable { testInjector = Guice.createInjector( new ReplTestModule(), new TorodServerTestModule(), new CoreModule(), new BackendModule(), new DerbyBackendModule(), new MetainfModule(), new D2RModule(), new MemoryTorodModule(), new MetricsModule(new MetricsConfig() { @Override public Boolean getMetricsEnabled() { return true; } }), new ConcurrentModule(), new MongoLayerModule(), new MongodServerTestModule(), specificModuleSupplier.get() ); torodServer = testInjector.getInstance(TorodBundle.class) .getTorodServer(); torodServer.startAsync(); mongodServer = testInjector.getInstance(MongodServer.class); mongodServer.startAsync(); mongodServer.awaitRunning(); assert mongodServer.getTorodServer().equals(torodServer); torodServer.awaitRunning(); oplogManager = testInjector.getInstance(OplogManager.class); oplogManager.startAsync(); oplogManager.awaitRunning(); aobe = testInjector.getInstance( AnalyzedOplogBatchExecutor.class); aobe.startAsync(); aobe.awaitRunning(); oplogApplier = testInjector.getInstance(OplogApplier.class); testContext = new DefaultOplogTestContext( mongodServer, oplogApplier ); } @Override protected void after() { if (oplogApplier != null) { try { oplogApplier.close(); } catch (Exception ex) { } } if (aobe != null) { aobe.stopAsync(); aobe.awaitTerminated(); } if (oplogManager != null) { oplogManager.stopAsync(); oplogManager.awaitTerminated(); } if (mongodServer != null) { mongodServer.stopAsync(); mongodServer.awaitTerminated(); } if (torodServer != null) { torodServer.stopAsync(); torodServer.awaitTerminated(); } } /** * A Guice module that simmulates the modules added on packaging project. */ private static class ReplTestModule extends AbstractModule { @Override protected void configure() { bind(Clock.class) .toInstance(Clock.systemUTC()); bind(DerbyDbBackendConfiguration.class) .toInstance(new TestDerbyDbBackendConfiguration()); bind(BuildProperties.class) .toInstance(new TestBuildProperties()); bind(Integer.class) .annotatedWith(ParallelLevel.class) .toInstance(Runtime.getRuntime().availableProcessors()); ThreadFactory threadFactory = new ThreadFactoryBuilder() .setNameFormat("torodb-executor-%d") .build(); bind(ThreadFactory.class) .toInstance(threadFactory); bind(ThreadFactory.class) .annotatedWith(TorodbIdleService.class) .toInstance(threadFactory); bind(ThreadFactory.class) .annotatedWith(TorodbRunnableService.class) .toInstance(threadFactory); bind(ThreadFactory.class) .annotatedWith(MongoWp.class) .toInstance(threadFactory); bind(ForkJoinWorkerThreadFactory.class) .toInstance(ForkJoinPool.defaultForkJoinWorkerThreadFactory); bind(MongodServerConfig.class) .toInstance(new MongodServerConfig(HostAndPort.fromParts("localhost", 28017))); bind(MongoServerConfig.class) .to(MongodServerConfig.class); bind(Supervisor.class) .annotatedWith(MongoDbRepl.class) .to(TestReplSupervisor.class); install(new ReplCommandsGuiceModule()); } private static class TestReplSupervisor implements Supervisor { @Override public SupervisorDecision onError(Object supervised, Throwable error) { LOGGER.error("Error on " + supervised, error); return SupervisorDecision.STOP; } } } private static class TestDerbyDbBackendConfiguration implements DerbyDbBackendConfiguration { @Override public boolean inMemory() { return true; } @Override public boolean embedded() { return true; } @Override public long getCursorTimeout() { return 10L * 60 * 1000; } @Override public long getConnectionPoolTimeout() { return 10_000; } @Override public int getConnectionPoolSize() { return 30; } @Override public int getReservedReadPoolSize() { return 10; } @Override public String getUsername() { return "torodb"; } @Override public String getPassword() { return null; } @Override public String getDbHost() { return "localhost"; } @Override public String getDbName() { return "torod"; } @Override public int getDbPort() { return 1527; } @Override public boolean includeForeignKeys() { return false; } } private static class MongodServerTestModule extends PrivateModule { @Override protected void configure() { bind(OplogApplier.class) .to(DefaultOplogApplier.class) .in(Singleton.class); expose(OplogApplier.class); bind(DefaultOplogApplier.BatchLimits.class) .toInstance(new BatchLimits(1000, Duration.ofSeconds(2))); } @Provides TorodServer getMongodServer(TorodBundle bundle) { return bundle.getTorodServer(); } } private static class TorodServerTestModule extends AbstractModule { @Override protected void configure() { bind(DbCloner.class) .annotatedWith(MongoDbRepl.class) .toProvider(AkkaDbClonerProvider.class); bind(CommitHeuristic.class) .to(DefaultCommitHeuristic.class) .in(Singleton.class); bind(Integer.class) .annotatedWith(DocsPerTransaction.class) .toInstance(1000); bind(ThreadFactory.class) .annotatedWith(MongoDbRepl.class) .toInstance(new ThreadFactoryBuilder() .setNameFormat("repl-unnamed-%d") .build() ); bind(DefaultConcurrentToolsFactory.BlockerThreadFactoryFunction.class) .toInstance(new BlockerThreadFactoryFunction() { @Override public ThreadFactory apply(String prefix) { return new ThreadFactoryBuilder() .setNameFormat(prefix + " -%d") .build(); } }); bind(DefaultConcurrentToolsFactory.ForkJoinThreadFactoryFunction.class) .toInstance(new ForkJoinThreadFactoryFunction() { @Override public ForkJoinWorkerThreadFactory apply(String prefix) { return new ForkJoinWorkerThreadFactory() { private volatile int idProvider = 0; @Override public ForkJoinWorkerThread newThread(ForkJoinPool pool) { ForkJoinWorkerThread newThread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); int id = idProvider++; newThread.setName(prefix + '-' + id); return newThread; } }; } }); } @Provides @Singleton BackendBundle createBackendBundle(BackendBundleFactory factory) { return factory.createBundle((o, t) -> SupervisorDecision.STOP); } @Provides @Singleton TorodBundle createTorodBundle(TorodBundleFactory factory, BackendBundle backendBundle) { return factory.createBundle((o, t) -> SupervisorDecision.STOP, backendBundle); } } private static class TestBuildProperties implements BuildProperties { public static final String BUILD_PROPERTIES_FILE = "ToroDB.build.properties"; public static final Pattern FULL_VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)(?:\\.(\\d+))?(?:-(.+))?"); private final String fullVersion; private final int majorVersion; private final int minorVersion; private final int subVersion; private final String extraVersion; private final Instant buildTime; private final String gitCommitId; private final String gitBranch; private final String gitRemoteOriginURL; private final String javaVersion; private final String javaVendor; private final String javaVMSpecificationVersion; private final String javaVMVersion; private final String osName; private final String osArch; private final String osVersion; public TestBuildProperties() { fullVersion = "3.2.0"; Matcher matcher = FULL_VERSION_PATTERN.matcher(fullVersion); if (!matcher.matches()) { throw new RuntimeException("Invalid version string '" + fullVersion + "'"); } majorVersion = Integer.parseInt(matcher.group(1)); minorVersion = Integer.parseInt(matcher.group(2)); subVersion = matcher.group(3) != null ? Integer.parseInt(matcher.group(3)) : 0; extraVersion = matcher.group(4); // DateUtils.parseDate may be replaced by SimpleDateFormat if using Java7 try { buildTime = Instant.now(); } catch (DateTimeParseException e) { throw new RuntimeException("buildTimestamp property not in ISO8601 format", e); } gitCommitId = "aCommitId"; gitBranch = "aGitBranch"; gitRemoteOriginURL = "aGitRemoteOriginURL"; javaVersion = "aJavaVersion"; javaVendor = "aJavaVendor"; javaVMSpecificationVersion = "aJavaVMSpecificationVersion"; javaVMVersion = "aJavaVMVersion"; osName = "aOsName"; osArch = "aOsArch"; osVersion = "aOsVersion"; } @Override public String getFullVersion() { return fullVersion; } @Override public int getMajorVersion() { return majorVersion; } @Override public int getMinorVersion() { return minorVersion; } @Override public int getSubVersion() { return subVersion; } @Override public String getExtraVersion() { return extraVersion; } @Override public Instant getBuildTime() { return buildTime; } @Override public String getGitCommitId() { return gitCommitId; } @Override public String getGitBranch() { return gitBranch; } public String getGitRemoteOriginUrl() { return gitRemoteOriginURL; } @Override public String getJavaVersion() { return javaVersion; } @Override public String getJavaVendor() { return javaVendor; } @Override public String getJavaVmSpecificationVersion() { return javaVMSpecificationVersion; } @Override public String getJavaVmVersion() { return javaVMVersion; } @Override public String getOsName() { return osName; } @Override public String getOsArch() { return osArch; } @Override public String getOsVersion() { return osVersion; } } }