Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.palantir.atlasdb.factory.TransactionManagers Maven / Gradle / Ivy
/*
* (c) Copyright 2018 Palantir Technologies Inc. All rights reserved.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package com.palantir.atlasdb.factory;
import com.codahale.metrics.MetricRegistry;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.palantir.async.initializer.AsyncInitializer;
import com.palantir.async.initializer.Callback;
import com.palantir.async.initializer.LambdaCallback;
import com.palantir.atlasdb.AtlasDbConstants;
import com.palantir.atlasdb.AtlasDbMetricNames;
import com.palantir.atlasdb.cache.DefaultTimestampCache;
import com.palantir.atlasdb.cache.TimestampCache;
import com.palantir.atlasdb.cell.api.TransactionKeyValueServiceManager;
import com.palantir.atlasdb.cleaner.CleanupFollower;
import com.palantir.atlasdb.cleaner.DefaultCleanerBuilder;
import com.palantir.atlasdb.cleaner.Follower;
import com.palantir.atlasdb.cleaner.GlobalClock;
import com.palantir.atlasdb.cleaner.KeyValueServicePuncherStore;
import com.palantir.atlasdb.cleaner.api.Cleaner;
import com.palantir.atlasdb.compact.BackgroundCompactor;
import com.palantir.atlasdb.compact.CompactorConfig;
import com.palantir.atlasdb.config.AtlasDbConfig;
import com.palantir.atlasdb.config.AtlasDbRuntimeConfig;
import com.palantir.atlasdb.config.AuxiliaryRemotingParameters;
import com.palantir.atlasdb.config.ImmutableAtlasDbConfig;
import com.palantir.atlasdb.config.ServerListConfig;
import com.palantir.atlasdb.config.SweepConfig;
import com.palantir.atlasdb.config.TimeLockClientConfig;
import com.palantir.atlasdb.config.TimeLockRequestBatcherProviders;
import com.palantir.atlasdb.coordination.CoordinationService;
import com.palantir.atlasdb.debug.ConflictTracer;
import com.palantir.atlasdb.debug.LockDiagnosticComponents;
import com.palantir.atlasdb.factory.startup.ConsistencyCheckRunner;
import com.palantir.atlasdb.factory.timestamp.FreshTimestampSupplierAdapter;
import com.palantir.atlasdb.http.AtlasDbRemotingConstants;
import com.palantir.atlasdb.internalschema.InternalSchemaMetadata;
import com.palantir.atlasdb.internalschema.TransactionSchemaInstaller;
import com.palantir.atlasdb.internalschema.TransactionSchemaManager;
import com.palantir.atlasdb.internalschema.metrics.MetadataCoordinationServiceMetrics;
import com.palantir.atlasdb.internalschema.persistence.CoordinationServices;
import com.palantir.atlasdb.keyvalue.api.CheckAndSetCompatibility;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
import com.palantir.atlasdb.keyvalue.api.TableReference;
import com.palantir.atlasdb.keyvalue.impl.DelegatingTransactionKeyValueServiceManager;
import com.palantir.atlasdb.keyvalue.impl.ProfilingKeyValueService;
import com.palantir.atlasdb.keyvalue.impl.SweepStatsKeyValueService;
import com.palantir.atlasdb.keyvalue.impl.TracingKeyValueService;
import com.palantir.atlasdb.keyvalue.impl.ValidatingQueryRewritingKeyValueService;
import com.palantir.atlasdb.logging.KvsProfilingLogger;
import com.palantir.atlasdb.memory.InMemoryAtlasDbConfig;
import com.palantir.atlasdb.schema.TargetedSweepSchema;
import com.palantir.atlasdb.schema.generated.SweepTableFactory;
import com.palantir.atlasdb.schema.generated.TargetedSweepTableFactory;
import com.palantir.atlasdb.spi.DerivedSnapshotConfig;
import com.palantir.atlasdb.spi.KeyValueServiceConfig;
import com.palantir.atlasdb.spi.KeyValueServiceManager;
import com.palantir.atlasdb.spi.SharedResourcesConfig;
import com.palantir.atlasdb.spi.TransactionKeyValueServiceConfig;
import com.palantir.atlasdb.spi.TransactionKeyValueServiceManagerFactory;
import com.palantir.atlasdb.spi.TransactionKeyValueServiceRuntimeConfig;
import com.palantir.atlasdb.sweep.AdjustableSweepBatchConfigSource;
import com.palantir.atlasdb.sweep.BackgroundSweeperImpl;
import com.palantir.atlasdb.sweep.BackgroundSweeperPerformanceLogger;
import com.palantir.atlasdb.sweep.CellsSweeper;
import com.palantir.atlasdb.sweep.ImmutableSweepBatchConfig;
import com.palantir.atlasdb.sweep.NoOpBackgroundSweeperPerformanceLogger;
import com.palantir.atlasdb.sweep.SpecificTableSweeper;
import com.palantir.atlasdb.sweep.SweepBatchConfig;
import com.palantir.atlasdb.sweep.SweepTaskRunner;
import com.palantir.atlasdb.sweep.metrics.LegacySweepMetrics;
import com.palantir.atlasdb.sweep.queue.MultiTableSweepQueueWriter;
import com.palantir.atlasdb.sweep.queue.TargetedSweeper;
import com.palantir.atlasdb.sweep.queue.clear.SafeTableClearerKeyValueService;
import com.palantir.atlasdb.sweep.queue.config.TargetedSweepInstallConfig;
import com.palantir.atlasdb.sweep.queue.config.TargetedSweepRuntimeConfig;
import com.palantir.atlasdb.table.description.Schema;
import com.palantir.atlasdb.timelock.adjudicate.feedback.TimeLockClientFeedbackService;
import com.palantir.atlasdb.transaction.ImmutableTransactionConfig;
import com.palantir.atlasdb.transaction.TransactionConfig;
import com.palantir.atlasdb.transaction.api.AtlasDbConstraintCheckingMode;
import com.palantir.atlasdb.transaction.api.LockWatchingCache;
import com.palantir.atlasdb.transaction.api.NoOpLockWatchingCache;
import com.palantir.atlasdb.transaction.api.TransactionManager;
import com.palantir.atlasdb.transaction.impl.ConflictDetectionManager;
import com.palantir.atlasdb.transaction.impl.ConflictDetectionManagers;
import com.palantir.atlasdb.transaction.impl.SerializableTransactionManager;
import com.palantir.atlasdb.transaction.impl.SweepStrategyManager;
import com.palantir.atlasdb.transaction.impl.SweepStrategyManagers;
import com.palantir.atlasdb.transaction.impl.TimelockTimestampServiceAdapter;
import com.palantir.atlasdb.transaction.impl.TransactionConstants;
import com.palantir.atlasdb.transaction.impl.consistency.ImmutableTimestampCorroborationConsistencyCheck;
import com.palantir.atlasdb.transaction.impl.metrics.DefaultMetricsFilterEvaluationContext;
import com.palantir.atlasdb.transaction.impl.metrics.MetricsFilterEvaluationContext;
import com.palantir.atlasdb.transaction.knowledge.AbandonedTimestampStoreImpl;
import com.palantir.atlasdb.transaction.knowledge.TransactionKnowledgeComponents;
import com.palantir.atlasdb.transaction.knowledge.coordinated.CoordinationAwareKnownAbandonedTransactionsStore;
import com.palantir.atlasdb.transaction.service.TransactionService;
import com.palantir.atlasdb.transaction.service.TransactionServices;
import com.palantir.atlasdb.util.AtlasDbMetrics;
import com.palantir.atlasdb.util.MetricsManager;
import com.palantir.atlasdb.util.MetricsManagers;
import com.palantir.atlasdb.versions.AtlasDbVersion;
import com.palantir.common.annotation.Output;
import com.palantir.common.annotations.ImmutablesStyles.StagedBuilderStyle;
import com.palantir.common.concurrent.PTExecutors;
import com.palantir.common.time.Clock;
import com.palantir.conjure.java.api.config.service.ServicesConfigBlock;
import com.palantir.conjure.java.api.config.service.UserAgent;
import com.palantir.conjure.java.api.config.service.UserAgents;
import com.palantir.dialogue.clients.DialogueClients;
import com.palantir.lock.LockClient;
import com.palantir.lock.LockRequest;
import com.palantir.lock.LockServerConfigs;
import com.palantir.lock.LockServerOptions;
import com.palantir.lock.LockService;
import com.palantir.lock.SimpleTimeDuration;
import com.palantir.lock.client.LockRefreshingLockService;
import com.palantir.lock.client.TimeLockClient;
import com.palantir.lock.client.metrics.TimeLockFeedbackBackgroundTask;
import com.palantir.lock.impl.LockServiceImpl;
import com.palantir.lock.v2.TimelockService;
import com.palantir.logsafe.DoNotLog;
import com.palantir.logsafe.Preconditions;
import com.palantir.logsafe.SafeArg;
import com.palantir.logsafe.exceptions.SafeIllegalArgumentException;
import com.palantir.logsafe.logger.SafeLogger;
import com.palantir.logsafe.logger.SafeLoggerFactory;
import com.palantir.refreshable.Refreshable;
import com.palantir.timestamp.ManagedTimestampService;
import com.palantir.timestamp.TimestampManagementService;
import com.palantir.timestamp.TimestampStoreInvalidator;
import com.palantir.tritium.metrics.registry.DefaultTaggedMetricRegistry;
import com.palantir.tritium.metrics.registry.TaggedMetricRegistry;
import com.palantir.util.EclipseCollections;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.immutables.value.Value;
@DoNotLog
@Value.Immutable
@StagedBuilderStyle
public abstract class TransactionManagers {
private static final SafeLogger log = SafeLoggerFactory.get(TransactionManagers.class);
public static final LockClient LOCK_CLIENT = LockClient.of("atlas instance");
abstract AtlasDbConfig config();
abstract Optional>> runtimeConfig();
/**
* Use {@link #runtimeConfig} instead.
*/
abstract Optional>> runtimeConfigSupplier();
abstract Set schemas();
/**
* This method is not used and should not be overriden from the default. It only exists because deleting
* it would be an ABI break.
*/
@Value.Default
Consumer registrar() {
return resource -> {};
}
@Value.Default
LockServerOptions lockServerOptions() {
return LockServerConfigs.DEFAULT;
}
@Value.Default
boolean allowHiddenTableAccess() {
return false;
}
@Value.Default
boolean validateLocksOnReads() {
return true;
}
@Value.Default
boolean lockImmutableTsOnReadOnlyTransactions() {
return false;
}
@Value.Default
boolean allSafeForLogging() {
return false;
}
@Value.Default
BiFunction defaultTimelockClientFactory() {
return (service, _management) -> TimeLockClient.createDefault(service);
}
abstract Optional lockAndTimestampServiceFactory();
abstract UserAgent userAgent();
abstract Optional timelockRequestBatcherProviders();
/**
* Please use {@link #globalTaggedMetricRegistry()} instead. The publishing of metrics to the external metrics
* registry has ceased; they are all published to the tagged registry instead.
*/
abstract MetricRegistry globalMetricsRegistry();
abstract TaggedMetricRegistry globalTaggedMetricRegistry();
/**
* The callback Runnable will be run when the TransactionManager is successfully initialized. The
* TransactionManager will stay uninitialized and continue to throw for all other purposes until the callback
* returns at which point it will become initialized. If asynchronous initialization is disabled, the callback will
* be run just before the TM is returned.
*
* Note that if the callback blocks forever, the TransactionManager will never become initialized, and calling its
* close() method will block forever as well. If the callback init() fails, and its cleanup() method throws,
* the TransactionManager will not become initialized and it will be closed.
*/
@Value.Default
Callback asyncInitializationCallback() {
return Callback.noOp();
}
// TODO(fdesouza): Remove this once PDS-95791 is resolved.
abstract Optional lockDiagnosticComponents();
@Value.Default
LockWatchingCache lockWatchingCache() {
return NoOpLockWatchingCache.INSTANCE;
}
@Value.Default
MetricsFilterEvaluationContext metricsFilterEvaluationContext() {
return DefaultMetricsFilterEvaluationContext.createDefault();
}
/**
* If set, a {@link com.palantir.dialogue.clients.DialogueClients.ReloadingFactory} that a
* {@link com.palantir.atlasdb.transaction.api.TransactionManager} based on this configuration should use.
* This may be useful for ensuring that connection pools are shared between multiple TransactionManagers in
* the same JVM.
* We intend this to be used in exactly one place - do not use this without discussing with the AtlasDB team.
*/
@Value.Default
DialogueClients.ReloadingFactory reloadingFactory() {
return newMinimalDialogueFactory();
}
public static ImmutableTransactionManagers.ConfigBuildStage builder() {
return ImmutableTransactionManagers.builder();
}
@SuppressWarnings("immutables:incompat")
@VisibleForTesting
static Consumer runAsync = task -> {
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
};
@Value.Check
protected void check() {
Preconditions.checkState(
!(runtimeConfigSupplier().isPresent() && runtimeConfig().isPresent()),
"Cannot provide both Refreshable and Supplier of runtime config");
EclipseCollections.loadClasses();
}
/**
* Accepts a single {@link Schema}.
*
* @see TransactionManagers#createInMemory(Set)
*/
public static TransactionManager createInMemory(Schema schema) {
return createInMemory(ImmutableSet.of(schema));
}
/**
* Create a {@link TransactionManager} backed by an
* {@link com.palantir.atlasdb.keyvalue.impl.InMemoryKeyValueService}. This should be used for testing
* purposes only.
*/
public static TransactionManager createInMemory(Set schemas) {
return createInMemory(schemas, Optional.empty());
}
public static TransactionManager createInMemory(Schema schema, LockAndTimestampServiceFactory factory) {
return createInMemory(Set.of(schema), Optional.of(factory));
}
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private static TransactionManager createInMemory(
Set schemas, Optional maybeFactory) {
AtlasDbConfig config = ImmutableAtlasDbConfig.builder()
.keyValueService(new InMemoryAtlasDbConfig())
.collectThreadDumpOnTimestampServiceInit(false)
.build();
return builder()
.config(config)
.userAgent(AtlasDbRemotingConstants.DEFAULT_USER_AGENT)
.globalMetricsRegistry(new MetricRegistry())
.globalTaggedMetricRegistry(DefaultTaggedMetricRegistry.getDefault())
.lockAndTimestampServiceFactory(maybeFactory)
.addAllSchemas(schemas)
.build()
.serializable();
}
@JsonIgnore
@Value.Derived
public TransactionManager serializable() {
List closeables = new ArrayList<>();
try {
return serializableInternal(closeables);
} catch (Throwable throwable) {
List closeablesClasses = closeables.stream()
.map(autoCloseable -> autoCloseable.getClass().toString())
.collect(Collectors.toList());
log.warn(
"Exception thrown when creating transaction manager. " + "Closing previously opened resources: {}",
SafeArg.of("classes", closeablesClasses.toString()),
throwable);
closeables.forEach(autoCloseable -> {
try {
autoCloseable.close();
} catch (Exception ex) {
log.info(
"Error closing {}",
SafeArg.of("class", autoCloseable.getClass().toString()),
ex);
}
});
throw throwable;
}
}
@SuppressWarnings("MethodLength")
private TransactionManager serializableInternal(@Output List closeables) {
MetricsManager metricsManager = setUpMetricsAndGetMetricsManager();
AtlasDbRuntimeConfigRefreshable runtimeConfigRefreshable =
initializeCloseable(() -> AtlasDbRuntimeConfigRefreshable.create(this), closeables);
Refreshable runtime = runtimeConfigRefreshable.config();
Optional timeLockFeedbackBackgroundTask =
getTimeLockFeedbackBackgroundTask(metricsManager, closeables, config(), runtime);
FreshTimestampSupplierAdapter adapter = new FreshTimestampSupplierAdapter();
KeyValueServiceConfig installConfig = config().keyValueService();
ServiceDiscoveringAtlasSupplier atlasFactory = new ServiceDiscoveringAtlasSupplier(
metricsManager,
installConfig,
runtime.map(AtlasDbRuntimeConfig::keyValueService),
config().namespace(),
Optional.empty(),
config().initializeAsync(),
config().collectThreadDumpOnTimestampServiceInit(),
adapter);
DerivedSnapshotConfig derivedSnapshotConfig = atlasFactory.getDerivedSnapshotConfig();
LockRequest.setDefaultLockTimeout(
SimpleTimeDuration.of(config().getDefaultLockTimeoutSeconds(), TimeUnit.SECONDS));
LockAndTimestampServiceFactory factory = lockAndTimestampServiceFactory()
.orElseGet(() -> new DefaultLockAndTimestampServiceFactory(
metricsManager,
config(),
runtime,
() -> LockServiceImpl.create(lockServerOptions()),
atlasFactory::getManagedTimestampService,
atlasFactory.getTimestampStoreInvalidator(),
userAgent(),
lockDiagnosticComponents(),
reloadingFactory(),
timeLockFeedbackBackgroundTask,
timelockRequestBatcherProviders(),
schemas(),
defaultTimelockClientFactory()));
LockAndTimestampServices lockAndTimestampServices = factory.createLockAndTimestampServices();
adapter.setTimestampService(lockAndTimestampServices.managedTimestampService());
KvsProfilingLogger.setSlowLogThresholdMillis(config().getKvsSlowLogThresholdMillis());
Refreshable sweepConfig = runtime.map(AtlasDbRuntimeConfig::sweep);
KeyValueService internalKeyValueService = initializeCloseable(
() -> {
KeyValueService kvs = atlasFactory.getKeyValueService(); // CassandraKeyValueServiceImpl
kvs = ProfilingKeyValueService.create(kvs); // Profiling, we think ok
kvs = new SafeTableClearerKeyValueService(
lockAndTimestampServices.timelock()::getImmutableTimestamp, kvs); // Autodelegate, ok
// Even if sweep queue writes are enabled, unless targeted sweep is enabled we generally still want
// to
// at least retain the option to perform background sweep, which requires updating the priority
// table.
if (!targetedSweepIsEnabled(runtime)) {
kvs = SweepStatsKeyValueService.create(
kvs,
new TimelockTimestampServiceAdapter(lockAndTimestampServices.timelock()),
sweepConfig.map(SweepConfig::writeThreshold),
sweepConfig.map(SweepConfig::writeSizeThreshold),
() -> true);
}
kvs = TracingKeyValueService.create(kvs); // ok
kvs = AtlasDbMetrics.instrumentTimed(
metricsManager.getRegistry(),
KeyValueService.class,
kvs,
MetricRegistry.name(KeyValueService.class)); // ok?
return ValidatingQueryRewritingKeyValueService.create(kvs); // ok
},
closeables);
KeyValueServiceManager keyValueServiceManager = new DefaultKeyValueServiceManager(metricsManager, adapter);
TransactionKeyValueServiceManager transactionKeyValueServiceManager = initializeCloseable(
() -> {
TransactionKeyValueServiceManager tkvsm;
if (config().transactionKeyValueService().isPresent()) {
TransactionKeyValueServiceManagerFactory tkvsmfFactory =
AtlasDbServiceDiscovery.createTransactionKeyValueServiceManagerFactoryOfCorrectType(
config().transactionKeyValueService().get());
tkvsm = createTransactionKeyValueServiceManager(
tkvsmfFactory,
metricsManager,
lockAndTimestampServices,
internalKeyValueService,
keyValueServiceManager,
config().transactionKeyValueService().get(),
runtimeConfig().get().map(optionalConfig -> optionalConfig
.get()
.transactionKeyValueService()
.get()),
config().initializeAsync());
} else {
tkvsm = new DelegatingTransactionKeyValueServiceManager(internalKeyValueService);
}
return tkvsm;
},
closeables);
TransactionManagersInitializer initializer = TransactionManagersInitializer.createInitialTables(
internalKeyValueService,
transactionKeyValueServiceManager,
schemas(),
config().initializeAsync(),
allSafeForLogging());
CleanupFollower follower = CleanupFollower.create(schemas());
CoordinationService coordinationService =
getSchemaMetadataCoordinationService(metricsManager, lockAndTimestampServices, internalKeyValueService);
TargetedSweeper targetedSweeper = uninitializedTargetedSweeper(
internalKeyValueService,
metricsManager,
config().targetedSweep(),
follower,
runtime.map(AtlasDbRuntimeConfig::targetedSweep),
coordinationService);
TransactionSchemaManager transactionSchemaManager = new TransactionSchemaManager(coordinationService);
TransactionKnowledgeComponents knowledge = TransactionKnowledgeComponents.create(
internalKeyValueService,
metricsManager.getTaggedRegistry(),
config().internalSchema(),
targetedSweeper::isInitialized);
TransactionComponents components = createTransactionComponents(
closeables, metricsManager, knowledge, transactionSchemaManager, internalKeyValueService, runtime);
TransactionService transactionService = components.transactionService();
ConflictDetectionManager conflictManager = ConflictDetectionManagers.create(internalKeyValueService);
SweepStrategyManager sweepStrategyManager = SweepStrategyManagers.createDefault(internalKeyValueService);
Cleaner cleaner = initializeCloseable(
() -> new DefaultCleanerBuilder(
internalKeyValueService,
lockAndTimestampServices.timelock(),
ImmutableList.of(follower),
transactionService,
metricsManager)
.setBackgroundScrubAggressively(config().backgroundScrubAggressively())
.setBackgroundScrubBatchSize(config().getBackgroundScrubBatchSize())
.setBackgroundScrubFrequencyMillis(config().getBackgroundScrubFrequencyMillis())
.setBackgroundScrubThreads(config().getBackgroundScrubThreads())
.setPunchIntervalMillis(config().getPunchIntervalMillis())
.setTransactionReadTimeout(config().getTransactionReadTimeoutMillis())
.setInitializeAsync(config().initializeAsync())
.buildCleaner(),
closeables);
MultiTableSweepQueueWriter targetedSweep = initializeCloseable(() -> targetedSweeper, closeables);
Supplier transactionConfigSupplier =
runtime.map(AtlasDbRuntimeConfig::transaction).map(this::withConsolidatedGrabImmutableTsLockFlag);
TimestampCache timestampCache = config().timestampCache()
.orElseGet(() -> new DefaultTimestampCache(
metricsManager.getRegistry(), () -> runtime.get().getTimestampCacheSize()));
ConflictTracer conflictTracer = lockDiagnosticComponents()
.map(LockDiagnosticComponents::clientLockDiagnosticCollector)
.map(Function.identity())
.orElse(ConflictTracer.NO_OP);
Callback callbacks = new Callback.CallChain<>(List.of(
timelockConsistencyCheckCallback(
config(), runtime.get(), lockAndTimestampServices, internalKeyValueService),
targetedSweep.singleAttemptCallback(),
asyncInitializationCallback(),
createClearsTable(internalKeyValueService)));
TransactionManager transactionManager = initializeCloseable(
() -> SerializableTransactionManager.createInstrumented(
metricsManager,
transactionKeyValueServiceManager,
lockAndTimestampServices.timelock(),
lockAndTimestampServices.lockWatcher(),
lockAndTimestampServices.managedTimestampService(),
lockAndTimestampServices.lock(),
transactionService,
() -> AtlasDbConstraintCheckingMode.FULL_CONSTRAINT_CHECKING_THROWS_EXCEPTIONS,
conflictManager,
sweepStrategyManager,
cleaner,
() -> areTransactionManagerInitializationPrerequisitesSatisfied(
initializer, internalKeyValueService, lockAndTimestampServices),
allowHiddenTableAccess(),
derivedSnapshotConfig.concurrentGetRangesThreadPoolSize(),
derivedSnapshotConfig.defaultGetRangesConcurrency(),
config().initializeAsync(),
timestampCache,
targetedSweep,
callbacks,
validateLocksOnReads(),
transactionConfigSupplier,
conflictTracer,
metricsFilterEvaluationContext(),
installConfig.sharedResourcesConfig().map(SharedResourcesConfig::sharedGetRangesPoolSize),
knowledge),
closeables);
transactionManager.registerClosingCallback(runtimeConfigRefreshable::close);
timeLockFeedbackBackgroundTask.ifPresent(task -> transactionManager.registerClosingCallback(task::close));
lockAndTimestampServices.resources().forEach(transactionManager::registerClosingCallback);
transactionManager.registerClosingCallback(transactionService::close);
components
.schemaInstaller()
.ifPresent(installer -> transactionManager.registerClosingCallback(installer::close));
transactionManager.registerClosingCallback(targetedSweep::close);
initializeCloseable(
() -> initializeSweepBackgroundProcess(
metricsManager,
config(),
runtime,
internalKeyValueService,
transactionService,
follower,
transactionManager,
runBackgroundSweepProcess()),
closeables);
initializeCloseable(
initializeCompactBackgroundProcess(
metricsManager,
lockAndTimestampServices,
internalKeyValueService,
transactionManager,
runtime.map(AtlasDbRuntimeConfig::compact)),
closeables);
log.info("Successfully created, and now returning a transaction manager: this may not be fully initialised.");
return transactionManager;
}
private TransactionKeyValueServiceManager createTransactionKeyValueServiceManager(
TransactionKeyValueServiceManagerFactory factory,
MetricsManager metricsManager,
LockAndTimestampServices lockAndTimestampServices,
KeyValueService internalTablesKeyValueService,
KeyValueServiceManager keyValueServiceManager,
TransactionKeyValueServiceConfig config,
Refreshable runtimeConfigRefreshable,
boolean initializeAsync) {
CoordinationService coordinationService =
CoordinationServices.createTransactionKeyValueServiceManagerCoordinator(
factory.coordinationValueClass(),
internalTablesKeyValueService,
lockAndTimestampServices.managedTimestampService(),
metricsManager,
config().initializeAsync());
return factory.create(
config().namespace()
.orElseThrow(() -> new SafeIllegalArgumentException(
"Namespace must be set when using transactionKeyValueService")),
reloadingFactory()
.withUserAgent(userAgent().addAgent(AtlasDbRemotingConstants.ATLASDB_HTTP_CLIENT_AGENT)),
metricsManager,
coordinationService,
keyValueServiceManager,
config,
runtimeConfigRefreshable,
initializeAsync);
}
private MetricsManager setUpMetricsAndGetMetricsManager() {
MetricRegistry internalAtlasDbMetrics = new MetricRegistry();
TaggedMetricRegistry internalTaggedAtlasDbMetrics = new DefaultTaggedMetricRegistry();
MetricsManager metricsManager = MetricsManagers.of(
internalAtlasDbMetrics,
internalTaggedAtlasDbMetrics,
runtimeConfig()
.map(runtimeConfigRefreshable -> runtimeConfigRefreshable.map(maybeRuntime -> maybeRuntime
.map(AtlasDbRuntimeConfig::enableMetricFiltering)
.orElse(true)))
.orElseGet(() -> Refreshable.only(true)));
globalTaggedMetricRegistry()
.addMetrics(
AtlasDbMetricNames.LIBRARY_ORIGIN_TAG,
AtlasDbMetricNames.LIBRARY_ORIGIN_VALUE,
metricsManager.getPublishableMetrics());
return metricsManager;
}
private Optional getTimeLockFeedbackBackgroundTask(
MetricsManager metricsManager,
@Output List closeables,
AtlasDbConfig config,
Refreshable runtimeConfig) {
if (isUsingTimeLock(config, runtimeConfig.current()) && shouldGiveFeedbackToServer(config)) {
Refreshable> refreshableTimeLockClientFeedbackServices =
getTimeLockClientFeedbackServices(config, runtimeConfig, userAgent(), reloadingFactory());
Refreshable timelockNodeCountSupplier =
DefaultLockAndTimestampServiceFactory.getServerListConfigSupplierForTimeLock(config, runtimeConfig)
.map(ServerListConfig::servers)
.map(Set::size);
return Optional.of(initializeCloseable(
() -> TimeLockFeedbackBackgroundTask.create(
metricsManager.getTaggedRegistry(),
AtlasDbVersion::readVersion,
serviceName(),
refreshableTimeLockClientFeedbackServices,
timelockNodeCountSupplier,
namespace()),
closeables));
}
return Optional.empty();
}
@VisibleForTesting
static Refreshable> getTimeLockClientFeedbackServices(
AtlasDbConfig config,
Refreshable runtimeConfig,
UserAgent userAgent,
DialogueClients.ReloadingFactory reloadingFactory) {
Refreshable serverListConfigSupplier =
DefaultLockAndTimestampServiceFactory.getServerListConfigSupplierForTimeLock(config, runtimeConfig);
BroadcastDialogueClientFactory broadcastDialogueClientFactory = BroadcastDialogueClientFactory.create(
reloadingFactory,
serverListConfigSupplier,
userAgent,
AuxiliaryRemotingParameters.builder()
.shouldRetry(true)
.userAgent(userAgent)
.shouldLimitPayload(true)
.build());
return broadcastDialogueClientFactory.getSingleNodeProxies(TimeLockClientFeedbackService.class, true);
}
private static DialogueClients.ReloadingFactory newMinimalDialogueFactory() {
return DialogueClients.create(Refreshable.only(ServicesConfigBlock.empty()))
.withBlockingExecutor(PTExecutors.newCachedThreadPool("atlas-dialogue-blocking"));
}
abstract Optional serviceIdentifierOverride();
@VisibleForTesting
@Value.Derived
String serviceName() {
return serviceIdentifierOverride().orElseGet(this::namespace);
}
@Value.Derived
String namespace() {
return Stream.of(
config().namespace(),
config().timelock().flatMap(TimeLockClientConfig::client),
config().keyValueService().namespace())
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst()
.orElse("UNKNOWN");
}
private static Callback createClearsTable(KeyValueService internalKeyValueService) {
TableReference clearsTableRef =
TargetedSweepTableFactory.of().getTableClearsTable(null).getTableRef();
byte[] clearsTableMetadata = TargetedSweepSchema.INSTANCE
.getLatestSchema()
.getAllTablesAndIndexMetadata()
.get(clearsTableRef)
.persistToBytes();
return LambdaCallback.of(tm -> internalKeyValueService.createTable(clearsTableRef, clearsTableMetadata));
}
/**
* If we decide to move a service to use thorough sweep; we need to make sure that background sweep won't cause any
* trouble by deleting large number of empty values at once - causing Cassandra OOMs.
*
* lockImmutableTsOnReadOnlyTransaction flag is used to decide on disabling background sweep, as this flag is used
* as an intermediate step for migrating to thorough sweep.
*
* Separately, users may disable the background sweep process entirely in config, which we respect.
*/
private boolean runBackgroundSweepProcess() {
return config().runBackgroundSweepProcess() && !lockImmutableTsOnReadOnlyTransactions();
}
@VisibleForTesting
TransactionConfig withConsolidatedGrabImmutableTsLockFlag(TransactionConfig transactionConfig) {
return ImmutableTransactionConfig.copyOf(transactionConfig)
.withLockImmutableTsOnReadOnlyTransactions(lockImmutableTsOnReadOnlyTransactions()
|| transactionConfig.lockImmutableTsOnReadOnlyTransactions());
}
private static boolean targetedSweepIsEnabled(Supplier runtime) {
return runtime.get().targetedSweep().enabled();
}
private TransactionComponents createTransactionComponents(
@Output List closeables,
MetricsManager metricsManager,
TransactionKnowledgeComponents knowledgeCache,
TransactionSchemaManager transactionSchemaManager,
KeyValueService keyValueService,
Supplier runtimeConfigSupplier) {
TransactionService transactionService = initializeCloseable(
() -> AtlasDbMetrics.instrumentTimed(
metricsManager.getRegistry(),
TransactionService.class,
TransactionServices.createTransactionService(
keyValueService,
transactionSchemaManager,
knowledgeCache,
metricsManager.getTaggedRegistry(),
() -> runtimeConfigSupplier
.get()
.internalSchema()
.acceptStagingReadsOnVersionThree())),
closeables);
Optional schemaInstaller = getTransactionSchemaInstallerIfSupported(
closeables, keyValueService, runtimeConfigSupplier, transactionSchemaManager);
return ImmutableTransactionComponents.builder()
.transactionService(transactionService)
.schemaInstaller(schemaInstaller)
.build();
}
private static Optional getTransactionSchemaInstallerIfSupported(
@Output List closeables,
KeyValueService keyValueService,
Supplier runtimeConfigSupplier,
TransactionSchemaManager transactionSchemaManager) {
CheckAndSetCompatibility compatibility = keyValueService.getCheckAndSetCompatibility();
if (compatibility.supportsCheckAndSetOperations() && compatibility.supportsDetailOnFailure()) {
return Optional.of(
initializeTransactionSchemaInstaller(closeables, runtimeConfigSupplier, transactionSchemaManager));
}
runtimeConfigSupplier
.get()
.internalSchema()
.targetTransactionsSchemaVersion()
.filter(version -> version != TransactionConstants.DIRECT_ENCODING_TRANSACTIONS_SCHEMA_VERSION)
.ifPresent(version -> log.warn(
"This service seems like it has been configured to use transaction schema version {},"
+ " which isn't supported as your KVS doesn't support details on CAS failures"
+ " (typically Postgres or Oracle). We will remain with transactions1.",
SafeArg.of("configuredTransactionSchemaVersion", version)));
return Optional.empty();
}
private static TransactionSchemaInstaller initializeTransactionSchemaInstaller(
@Output List closeables,
Supplier runtimeConfigSupplier,
TransactionSchemaManager transactionSchemaManager) {
return initializeCloseable(
() -> TransactionSchemaInstaller.createStarted(
transactionSchemaManager,
() -> runtimeConfigSupplier.get().internalSchema().targetTransactionsSchemaVersion()),
closeables);
}
private CoordinationService getSchemaMetadataCoordinationService(
MetricsManager metricsManager,
LockAndTimestampServices lockAndTimestampServices,
KeyValueService keyValueService) {
CoordinationService metadataCoordinationService = CoordinationServices.createDefault(
keyValueService,
lockAndTimestampServices.managedTimestampService(),
metricsManager,
config().initializeAsync());
MetadataCoordinationServiceMetrics.registerMetrics(
metricsManager, metadataCoordinationService, lockAndTimestampServices.managedTimestampService());
return metadataCoordinationService;
}
private static Optional initializeCompactBackgroundProcess(
MetricsManager metricsManager,
LockAndTimestampServices lockAndTimestampServices,
KeyValueService keyValueService,
TransactionManager transactionManager,
Supplier compactorConfigSupplier) {
Optional backgroundCompactorOptional = BackgroundCompactor.createAndRun(
metricsManager,
transactionManager,
keyValueService,
lockAndTimestampServices.lock(),
compactorConfigSupplier);
backgroundCompactorOptional.ifPresent(
backgroundCompactor -> transactionManager.registerClosingCallback(backgroundCompactor::close));
return backgroundCompactorOptional;
}
private static T initializeCloseable(
Supplier closeableSupplier, @Output List closeables) {
T ret = closeableSupplier.get();
closeables.add(ret);
return ret;
}
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private static Optional initializeCloseable(
Optional closeableOptional, @Output List closeables) {
closeableOptional.ifPresent(closeables::add);
return closeableOptional;
}
private static boolean areTransactionManagerInitializationPrerequisitesSatisfied(
AsyncInitializer initializer,
KeyValueService internalKeyValueService,
LockAndTimestampServices lockAndTimestampServices) {
return initializer.isInitialized()
&& internalKeyValueService.isInitialized()
&& timeLockMigrationCompleteIfNeeded(lockAndTimestampServices);
}
@VisibleForTesting
static boolean timeLockMigrationCompleteIfNeeded(LockAndTimestampServices lockAndTimestampServices) {
return lockAndTimestampServices
.migrator()
.map(AsyncInitializer::isInitialized)
.orElse(true);
}
private static BackgroundSweeperImpl initializeSweepBackgroundProcess(
MetricsManager metricsManager,
AtlasDbConfig config,
Supplier runtimeConfigSupplier,
KeyValueService kvs,
TransactionService transactionService,
CleanupFollower follower,
TransactionManager transactionManager,
boolean runInBackground) {
CellsSweeper cellsSweeper = new CellsSweeper(transactionManager, kvs, ImmutableList.of(follower));
LegacySweepMetrics sweepMetrics = new LegacySweepMetrics(metricsManager.getRegistry());
SweepTaskRunner sweepRunner = new SweepTaskRunner(
kvs,
transactionManager::getUnreadableTimestamp,
transactionManager::getImmutableTimestamp,
transactionService,
cellsSweeper,
sweepMetrics);
BackgroundSweeperPerformanceLogger sweepPerfLogger = new NoOpBackgroundSweeperPerformanceLogger();
AdjustableSweepBatchConfigSource sweepBatchConfigSource = AdjustableSweepBatchConfigSource.create(
metricsManager,
() -> getSweepBatchConfig(runtimeConfigSupplier.get().sweep()));
SpecificTableSweeper specificTableSweeper = SpecificTableSweeper.create(
transactionManager,
kvs,
sweepRunner,
SweepTableFactory.of(),
sweepPerfLogger,
sweepMetrics,
config.initializeAsync());
BackgroundSweeperImpl backgroundSweeper = BackgroundSweeperImpl.create(
metricsManager,
sweepBatchConfigSource,
() -> runtimeConfigSupplier.get().sweep().enabled(),
() -> runtimeConfigSupplier.get().sweep().sweepThreads(),
() -> runtimeConfigSupplier.get().sweep().pauseMillis(),
() -> runtimeConfigSupplier.get().sweep().sweepPriorityOverrides(),
specificTableSweeper);
transactionManager.registerClosingCallback(backgroundSweeper::shutdown);
if (runInBackground) {
backgroundSweeper.runInBackground();
}
return backgroundSweeper;
}
private static SweepBatchConfig getSweepBatchConfig(SweepConfig sweepConfig) {
return ImmutableSweepBatchConfig.builder()
.maxCellTsPairsToExamine(sweepConfig.readLimit())
.candidateBatchSize(
sweepConfig.candidateBatchHint().orElse(AtlasDbConstants.DEFAULT_SWEEP_CANDIDATE_BATCH_HINT))
.deleteBatchSize(sweepConfig.deleteBatchHint())
.build();
}
private static Callback timelockConsistencyCheckCallback(
AtlasDbConfig atlasDbConfig,
AtlasDbRuntimeConfig initialRuntimeConfig,
LockAndTimestampServices lockAndTimestampServices,
KeyValueService internalKeyValueService) {
// Only do the consistency check if we're using TimeLock.
// This avoids a bootstrapping problem with leader-block services without async initialisation,
// where you need a working timestamp service to check consistency, you need to check consistency
// before you can return a TM, you need to return a TM to listen on ports, and you need to listen on
// ports in order to get a working timestamp service.
if (isUsingTimeLock(atlasDbConfig, initialRuntimeConfig)) {
ToLongFunction conservativeBoundSupplier = txnManager -> {
Clock clock = GlobalClock.create(txnManager.getTimelockService());
return KeyValueServicePuncherStore.get(internalKeyValueService, clock.getTimeMillis());
};
return ConsistencyCheckRunner.create(ImmutableTimestampCorroborationConsistencyCheck.builder()
.conservativeBound(conservativeBoundSupplier)
.freshTimestampSource(
unused -> lockAndTimestampServices.timelock().getFreshTimestamp())
.build());
}
return Callback.noOp();
}
// TODO(gs): wrap install and runtime config objects to avoid passing them both in?
static boolean isUsingTimeLock(AtlasDbConfig atlasDbConfig, AtlasDbRuntimeConfig runtimeConfig) {
return atlasDbConfig.timelock().isPresent()
|| runtimeConfig.timelockRuntime().isPresent();
}
private boolean shouldGiveFeedbackToServer(AtlasDbConfig config) {
return config.timelock()
.map(TimeLockClientConfig::shouldGiveFeedbackToTimeLockServer)
.orElse(false);
}
/**
* This method should not be used directly. It remains here to support the AtlasDB-Dagger module and the CLIs, but
* may be removed at some point in the future.
*
* @deprecated Not intended for public use outside of the AtlasDB CLIs
*/
@Deprecated
public static LockAndTimestampServices createLockAndTimestampServicesForCli(
MetricsManager metricsManager,
AtlasDbConfig config,
Refreshable runtimeConfigSupplier,
Consumer registrar,
Supplier lock,
Supplier time,
TimestampStoreInvalidator invalidator,
String userAgent) {
LockAndTimestampServices lockAndTimestampServices =
DefaultLockAndTimestampServiceFactory.createRawInstrumentedServices(
metricsManager,
config,
runtimeConfigSupplier,
lock,
time,
invalidator,
UserAgents.tryParse(userAgent),
Optional.empty(),
newMinimalDialogueFactory(),
Optional.empty(),
Optional.empty(),
ImmutableSet.of());
TimeLockClient timeLockClient = TimeLockClient.withSynchronousUnlocker(lockAndTimestampServices.timelock());
return ImmutableLockAndTimestampServices.builder()
.from(lockAndTimestampServices)
.timelock(timeLockClient)
.lock(LockRefreshingLockService.create(lockAndTimestampServices.lock()))
.addResources(timeLockClient::close)
.build();
}
private static TargetedSweeper uninitializedTargetedSweeper(
KeyValueService kvs,
MetricsManager metricsManager,
TargetedSweepInstallConfig install,
Follower follower,
Supplier runtime,
CoordinationService coordinationService) {
CoordinationAwareKnownAbandonedTransactionsStore abandonedTxnStore =
new CoordinationAwareKnownAbandonedTransactionsStore(
coordinationService, new AbandonedTimestampStoreImpl(kvs));
return TargetedSweeper.createUninitialized(
metricsManager,
runtime,
install,
ImmutableList.of(follower),
abandonedTxnStore::addAbandonedTimestamps,
kvs);
}
@Value.Immutable
public interface TransactionComponents {
TransactionService transactionService();
Optional schemaInstaller();
}
}