All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.palantir.atlasdb.factory.DefaultLockAndTimestampServiceFactory Maven / Gradle / Ivy

There is a newer version: 0.1152.0
Show newest version
/*
 * (c) Copyright 2021 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.google.common.base.Suppliers;
import com.palantir.atlasdb.config.AtlasDbConfig;
import com.palantir.atlasdb.config.AtlasDbRuntimeConfig;
import com.palantir.atlasdb.config.ServerListConfig;
import com.palantir.atlasdb.config.ServerListConfigs;
import com.palantir.atlasdb.config.TimeLockClientConfig;
import com.palantir.atlasdb.config.TimeLockRequestBatcherProviders;
import com.palantir.atlasdb.debug.LockDiagnosticComponents;
import com.palantir.atlasdb.debug.LockDiagnosticConjureTimelockService;
import com.palantir.atlasdb.factory.startup.TimeLockMigrator;
import com.palantir.atlasdb.keyvalue.api.LockWatchCachingConfig;
import com.palantir.atlasdb.keyvalue.api.watch.LockWatchManagerInternal;
import com.palantir.atlasdb.table.description.Schema;
import com.palantir.atlasdb.timelock.api.ConjureTimelockService;
import com.palantir.atlasdb.transaction.impl.InstrumentedTimelockService;
import com.palantir.atlasdb.transaction.impl.TimelockTimestampServiceAdapter;
import com.palantir.atlasdb.util.MetricsManager;
import com.palantir.conjure.java.api.config.service.UserAgent;
import com.palantir.dialogue.clients.DialogueClients.ReloadingFactory;
import com.palantir.lock.LockService;
import com.palantir.lock.NamespaceAgnosticLockRpcClient;
import com.palantir.lock.client.AuthenticatedInternalMultiClientConjureTimelockService;
import com.palantir.lock.client.ImmutableMultiClientRequestBatchers;
import com.palantir.lock.client.InternalMultiClientConjureTimelockService;
import com.palantir.lock.client.LeaderElectionReportingTimelockService;
import com.palantir.lock.client.LeaderTimeCoalescingBatcher;
import com.palantir.lock.client.LeaderTimeGetter;
import com.palantir.lock.client.LegacyLeaderTimeGetter;
import com.palantir.lock.client.LegacyLockTokenUnlocker;
import com.palantir.lock.client.LockTokenUnlocker;
import com.palantir.lock.client.MultiClientTimeLockUnlocker;
import com.palantir.lock.client.NamespacedCoalescingLeaderTimeGetter;
import com.palantir.lock.client.NamespacedConjureLockWatchingService;
import com.palantir.lock.client.NamespacedConjureTimelockService;
import com.palantir.lock.client.NamespacedLockTokenUnlocker;
import com.palantir.lock.client.ProfilingTimelockService;
import com.palantir.lock.client.ReferenceTrackingWrapper;
import com.palantir.lock.client.RemoteLockServiceAdapter;
import com.palantir.lock.client.RemoteTimelockServiceAdapter;
import com.palantir.lock.client.RequestBatchersFactory;
import com.palantir.lock.client.TimeLockClient;
import com.palantir.lock.client.TimestampCorroboratingTimelockService;
import com.palantir.lock.client.metrics.TimeLockFeedbackBackgroundTask;
import com.palantir.lock.impl.LegacyTimelockService;
import com.palantir.lock.v2.DefaultNamespacedTimelockRpcClient;
import com.palantir.lock.v2.NamespacedTimelockRpcClient;
import com.palantir.lock.v2.TimelockRpcClient;
import com.palantir.lock.v2.TimelockService;
import com.palantir.logsafe.exceptions.SafeIllegalArgumentException;
import com.palantir.logsafe.exceptions.SafeIllegalStateException;
import com.palantir.refreshable.Refreshable;
import com.palantir.timestamp.ManagedTimestampService;
import com.palantir.timestamp.RemoteTimestampManagementAdapter;
import com.palantir.timestamp.TimestampManagementService;
import com.palantir.timestamp.TimestampService;
import com.palantir.timestamp.TimestampStoreInvalidator;
import com.palantir.util.OptionalResolver;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Supplier;

public final class DefaultLockAndTimestampServiceFactory implements LockAndTimestampServiceFactory {
    private final MetricsManager metricsManager;
    private final AtlasDbConfig config;
    private final Refreshable runtimeConfig;
    private final Supplier lock;
    private final Supplier time;
    private final TimestampStoreInvalidator invalidator;
    private final UserAgent userAgent;
    private final Optional lockDiagnosticComponents;
    private final ReloadingFactory reloadingFactory;
    private final Optional timeLockFeedbackBackgroundTask;
    private final Optional timelockRequestBatcherProviders;
    private final Set schemas;

    private final BiFunction timelockClientFactory;

    public DefaultLockAndTimestampServiceFactory(
            MetricsManager metricsManager,
            AtlasDbConfig config,
            Refreshable runtimeConfig,
            Supplier lock,
            Supplier time,
            TimestampStoreInvalidator invalidator,
            UserAgent userAgent,
            Optional lockDiagnosticComponents,
            ReloadingFactory reloadingFactory,
            Optional timeLockFeedbackBackgroundTask,
            Optional timelockRequestBatcherProviders,
            Set schemas) {
        this(
                metricsManager,
                config,
                runtimeConfig,
                lock,
                time,
                invalidator,
                userAgent,
                lockDiagnosticComponents,
                reloadingFactory,
                timeLockFeedbackBackgroundTask,
                timelockRequestBatcherProviders,
                schemas,
                (service, _management) -> TimeLockClient.createDefault(service));
    }

    public DefaultLockAndTimestampServiceFactory(
            MetricsManager metricsManager,
            AtlasDbConfig config,
            Refreshable runtimeConfig,
            Supplier lock,
            Supplier time,
            TimestampStoreInvalidator invalidator,
            UserAgent userAgent,
            Optional lockDiagnosticComponents,
            ReloadingFactory reloadingFactory,
            Optional timeLockFeedbackBackgroundTask,
            Optional timelockRequestBatcherProviders,
            Set schemas,
            BiFunction timelockClientFactory) {
        this.metricsManager = metricsManager;
        this.config = config;
        this.runtimeConfig = runtimeConfig;
        this.lock = lock;
        this.time = time;
        this.invalidator = invalidator;
        this.userAgent = userAgent;
        this.lockDiagnosticComponents = lockDiagnosticComponents;
        this.reloadingFactory = reloadingFactory;
        this.timeLockFeedbackBackgroundTask = timeLockFeedbackBackgroundTask;
        this.timelockRequestBatcherProviders = timelockRequestBatcherProviders;
        this.schemas = schemas;
        this.timelockClientFactory = timelockClientFactory;
    }

    @Override
    public LockAndTimestampServices createLockAndTimestampServices() {
        LockAndTimestampServices lockAndTimestampServices = createRawInstrumentedServices(
                metricsManager,
                config,
                runtimeConfig,
                lock,
                time,
                invalidator,
                userAgent,
                lockDiagnosticComponents,
                reloadingFactory,
                timeLockFeedbackBackgroundTask,
                timelockRequestBatcherProviders,
                schemas);
        return withMetrics(metricsManager, withDecoratedLockService(lockAndTimestampServices));
    }

    private LockAndTimestampServices withDecoratedLockService(LockAndTimestampServices lockAndTimestampServices) {
        TimeLockClient timeLockClient = timelockClientFactory.apply(
                lockAndTimestampServices.timelock(), lockAndTimestampServices.managedTimestampService());
        ProfilingTimelockService profilingService = ProfilingTimelockService.create(timeLockClient);
        return ImmutableLockAndTimestampServices.builder()
                .from(lockAndTimestampServices)
                .timestamp(new TimelockTimestampServiceAdapter(profilingService))
                .timelock(profilingService)
                .lock(LockServices.wrapWithDefaultDecorators(lockAndTimestampServices.lock()))
                .addResources(timeLockClient::close)
                .addResources(profilingService::close)
                .build();
    }

    private static LockAndTimestampServices withMetrics(
            MetricsManager metricsManager, LockAndTimestampServices lockAndTimestampServices) {
        TimelockService timelockServiceWithBatching = lockAndTimestampServices.timelock();
        TimelockService instrumentedTimelockService =
                InstrumentedTimelockService.create(timelockServiceWithBatching, metricsManager);

        return ImmutableLockAndTimestampServices.builder()
                .from(lockAndTimestampServices)
                .timestamp(new TimelockTimestampServiceAdapter(instrumentedTimelockService))
                .timelock(instrumentedTimelockService)
                .build();
    }

    @SuppressWarnings("TooManyArguments") // Legacy
    static LockAndTimestampServices createRawInstrumentedServices(
            MetricsManager metricsManager,
            AtlasDbConfig config,
            Refreshable runtimeConfig,
            Supplier lock,
            Supplier time,
            TimestampStoreInvalidator invalidator,
            UserAgent userAgent,
            Optional lockDiagnosticComponents,
            ReloadingFactory reloadingFactory,
            Optional timeLockFeedbackBackgroundTask,
            Optional timelockRequestBatcherProviders,
            Set knownSchemas) {
        AtlasDbRuntimeConfig initialRuntimeConfig = runtimeConfig.get();
        assertNoSpuriousTimeLockBlockInRuntimeConfig(config, initialRuntimeConfig);
        if (config.leader().isPresent()) { // 1-node leader config
            // equivalent to raw embedded; multi-node leader is not supported
            return createRawEmbeddedServices(metricsManager, lock, time);
        } else if (config.timestamp().isPresent() && config.lock().isPresent()) {
            return createRawRemoteServices(metricsManager, config, runtimeConfig, userAgent);
        } else if (TransactionManagers.isUsingTimeLock(config, initialRuntimeConfig)) {
            return createRawServicesFromTimeLock(
                    metricsManager,
                    config,
                    runtimeConfig,
                    invalidator,
                    userAgent,
                    lockDiagnosticComponents,
                    reloadingFactory,
                    timeLockFeedbackBackgroundTask,
                    timelockRequestBatcherProviders,
                    knownSchemas);
        } else { // raw embedded, i.e. embedded with no leader config
            return createRawEmbeddedServices(metricsManager, lock, time);
        }
    }

    private static void assertNoSpuriousTimeLockBlockInRuntimeConfig(
            AtlasDbConfig config, AtlasDbRuntimeConfig initialRuntimeConfig) {
        // Note: The other direction (timelock install config without a runtime block) should be maintained for
        // backwards compatibility.
        if (config.remoteTimestampAndLockOrLeaderBlocksPresent()
                && initialRuntimeConfig.timelockRuntime().isPresent()) {
            throw new SafeIllegalStateException("Found a service configured not to use timelock, with a timelock"
                    + " block in the runtime config! This is unexpected. If you wish to use non-timelock services,"
                    + " please remove the timelock block from the runtime config; if you wish to use timelock,"
                    + " please remove the leader, remote timestamp or remote lock configuration blocks.");
        }
    }

    private static LockAndTimestampServices createRawServicesFromTimeLock(
            MetricsManager metricsManager,
            AtlasDbConfig config,
            Refreshable runtimeConfig,
            TimestampStoreInvalidator invalidator,
            UserAgent userAgent,
            Optional lockDiagnosticComponents,
            ReloadingFactory reloadingFactory,
            Optional timeLockFeedbackBackgroundTask,
            Optional timelockRequestBatcherProviders,
            Set schemas) {
        Refreshable serverListConfigSupplier =
                getServerListConfigSupplierForTimeLock(config, runtimeConfig);

        String timelockNamespace =
                OptionalResolver.resolve(config.timelock().flatMap(TimeLockClientConfig::client), config.namespace());
        LockAndTimestampServices lockAndTimestampServices = getLockAndTimestampServices(
                metricsManager,
                serverListConfigSupplier,
                userAgent,
                timelockNamespace,
                lockDiagnosticComponents,
                reloadingFactory,
                timeLockFeedbackBackgroundTask,
                timelockRequestBatcherProviders,
                schemas,
                config.lockWatchCaching());

        TimeLockMigrator migrator = TimeLockMigrator.create(
                lockAndTimestampServices.managedTimestampService(), invalidator, config.initializeAsync());
        migrator.migrate(); // This can proceed async if config.initializeAsync() was set

        return ImmutableLockAndTimestampServices.copyOf(lockAndTimestampServices)
                .withMigrator(migrator);
    }

    static Refreshable getServerListConfigSupplierForTimeLock(
            AtlasDbConfig config, Refreshable runtimeConfigSupplier) {
        return ServerListConfigs.getTimeLockServersFromAtlasDbConfig(config, runtimeConfigSupplier);
    }

    private static LockAndTimestampServices getLockAndTimestampServices(
            MetricsManager metricsManager,
            Refreshable timelockServerListConfig,
            UserAgent userAgent,
            String timelockNamespace,
            Optional lockDiagnosticComponents,
            ReloadingFactory reloadingFactory,
            Optional timeLockFeedbackBackgroundTask,
            Optional timelockRequestBatcherProviders,
            Set schemas,
            LockWatchCachingConfig cachingConfig) {
        AtlasDbDialogueServiceProvider serviceProvider = AtlasDbDialogueServiceProvider.create(
                timelockServerListConfig, reloadingFactory, userAgent, metricsManager.getTaggedRegistry());

        LockService lockService = LockServices.createRawLockServiceClient(
                serviceProvider, metricsManager.getRegistry(), timelockNamespace);

        ConjureTimelockService conjureTimelockService = serviceProvider.getConjureTimelockService();
        TimelockRpcClient timelockClient = serviceProvider.getTimelockRpcClient();

        // TODO(fdesouza): Remove this once PDS-95791 is resolved.
        ConjureTimelockService withDiagnosticsConjureTimelockService = lockDiagnosticComponents
                .map(components -> new LockDiagnosticConjureTimelockService(
                        conjureTimelockService,
                        components.clientLockDiagnosticCollector(),
                        components.localLockTracker()))
                .orElse(conjureTimelockService);

        NamespacedTimelockRpcClient namespacedTimelockRpcClient =
                new DefaultNamespacedTimelockRpcClient(timelockClient, timelockNamespace);
        LeaderElectionReportingTimelockService leaderElectionReportingTimelockService =
                LeaderElectionReportingTimelockService.create(withDiagnosticsConjureTimelockService, timelockNamespace);

        timeLockFeedbackBackgroundTask.ifPresent(
                task -> task.registerLeaderElectionStatistics(leaderElectionReportingTimelockService));

        NamespacedConjureTimelockService namespacedConjureTimelockService =
                TimestampCorroboratingTimelockService.create(
                        timelockNamespace, metricsManager.getTaggedRegistry(), leaderElectionReportingTimelockService);

        NamespacedConjureLockWatchingService lockWatchingService = new NamespacedConjureLockWatchingService(
                serviceProvider.getConjureLockWatchingService(), timelockNamespace);

        Supplier multiClientTimelockServiceSupplier =
                getMultiClientTimelockServiceSupplier(serviceProvider);

        Supplier> requestBatcherProvider =
                () -> timelockRequestBatcherProviders.map(batcherProviders -> ImmutableMultiClientRequestBatchers.of(
                        batcherProviders.commitTimestamps().getBatcher(multiClientTimelockServiceSupplier),
                        batcherProviders.startTransactions().getBatcher(multiClientTimelockServiceSupplier)));

        TimeLockHelperServices timeLockHelperServices = TimeLockHelperServices.create(
                timelockNamespace, metricsManager, schemas, lockWatchingService, cachingConfig, requestBatcherProvider);
        LockWatchManagerInternal lockWatchManager = timeLockHelperServices.lockWatchManager();

        RemoteTimelockServiceAdapter remoteTimelockServiceAdapter = RemoteTimelockServiceAdapter.create(
                namespacedTimelockRpcClient,
                namespacedConjureTimelockService,
                getLeaderTimeGetter(
                        timelockNamespace,
                        timelockRequestBatcherProviders,
                        namespacedConjureTimelockService,
                        multiClientTimelockServiceSupplier),
                timeLockHelperServices.requestBatchersFactory(),
                getTimeLockUnlocker(
                        timelockNamespace,
                        timelockRequestBatcherProviders,
                        namespacedConjureTimelockService,
                        multiClientTimelockServiceSupplier));
        TimestampManagementService timestampManagementService = new RemoteTimestampManagementAdapter(
                serviceProvider.getTimestampManagementRpcClient(), timelockNamespace);

        return ImmutableLockAndTimestampServices.builder()
                .lock(lockService)
                .timestamp(new TimelockTimestampServiceAdapter(remoteTimelockServiceAdapter))
                .timestampManagement(timestampManagementService)
                .timelock(remoteTimelockServiceAdapter)
                .lockWatcher(lockWatchManager)
                .addResources(remoteTimelockServiceAdapter::close)
                .addResources(lockWatchManager::close)
                .build();
    }

    // Note: There is some duplication in the following two methods, but extracting a common method requires a fairly
    // large amount of nontrivial state. Consider extracting a common method if this needs to be implemented again.
    private static LockTokenUnlocker getTimeLockUnlocker(
            String timelockNamespace,
            Optional timelockRequestBatcherProviders,
            NamespacedConjureTimelockService namespacedConjureTimelockService,
            Supplier multiClientTimelockServiceSupplier) {
        if (timelockRequestBatcherProviders.isEmpty()) {
            return new LegacyLockTokenUnlocker(namespacedConjureTimelockService);
        }
        ReferenceTrackingWrapper batcher =
                timelockRequestBatcherProviders.get().unlock().getBatcher(multiClientTimelockServiceSupplier);
        batcher.recordReference();
        return new NamespacedLockTokenUnlocker(timelockNamespace, batcher);
    }

    private static LeaderTimeGetter getLeaderTimeGetter(
            String timelockNamespace,
            Optional timelockRequestBatcherProviders,
            NamespacedConjureTimelockService namespacedConjureTimelockService,
            Supplier multiClientTimelockServiceSupplier) {

        if (!timelockRequestBatcherProviders.isPresent()) {
            return new LegacyLeaderTimeGetter(namespacedConjureTimelockService);
        }

        ReferenceTrackingWrapper referenceTrackingBatcher =
                timelockRequestBatcherProviders.get().leaderTime().getBatcher(multiClientTimelockServiceSupplier);
        referenceTrackingBatcher.recordReference();
        return new NamespacedCoalescingLeaderTimeGetter(timelockNamespace, referenceTrackingBatcher);
    }

    private static Supplier getMultiClientTimelockServiceSupplier(
            AtlasDbDialogueServiceProvider serviceProvider) {
        return Suppliers.memoize(() -> new AuthenticatedInternalMultiClientConjureTimelockService(
                serviceProvider.getMultiClientConjureTimelockService()));
    }

    private static LockAndTimestampServices createRawRemoteServices(
            MetricsManager metricsManager,
            AtlasDbConfig config,
            Supplier runtimeConfigSupplier,
            UserAgent userAgent) {
        ServiceCreator creator = ServiceCreator.noPayloadLimiter(
                metricsManager,
                Refreshable.only(config.lock()
                        .orElseThrow(() -> new SafeIllegalArgumentException("lock server list config absent"))),
                userAgent,
                () -> runtimeConfigSupplier.get().remotingClient());
        LockService lockService =
                new RemoteLockServiceAdapter(creator.createService(NamespaceAgnosticLockRpcClient.class));
        TimestampService timeService = creator.createService(TimestampService.class);
        TimestampManagementService timestampManagementService = creator.createService(TimestampManagementService.class);

        return ImmutableLockAndTimestampServices.builder()
                .lock(lockService)
                .timestamp(timeService)
                .timestampManagement(timestampManagementService)
                .timelock(new LegacyTimelockService(timeService, lockService, TransactionManagers.LOCK_CLIENT))
                .build();
    }

    private static LockAndTimestampServices createRawEmbeddedServices(
            MetricsManager metricsManager,
            Supplier lock,
            Supplier managedTimestampServiceSupplier) {
        LockService lockService =
                ServiceCreator.instrumentService(metricsManager.getRegistry(), lock.get(), LockService.class);

        ManagedTimestampService managedTimestampService = managedTimestampServiceSupplier.get();

        TimestampService timeService = ServiceCreator.instrumentService(
                metricsManager.getRegistry(), managedTimestampService, TimestampService.class);
        TimestampManagementService timestampManagementService = ServiceCreator.instrumentService(
                metricsManager.getRegistry(), managedTimestampService, TimestampManagementService.class);

        return ImmutableLockAndTimestampServices.builder()
                .lock(lockService)
                .timestamp(timeService)
                .timestampManagement(timestampManagementService)
                .timelock(new LegacyTimelockService(timeService, lockService, TransactionManagers.LOCK_CLIENT))
                .build();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy