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

com.azure.cosmos.ChangeFeedProcessorBuilder Maven / Gradle / Ivy

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.cosmos;

import com.azure.cosmos.implementation.apachecommons.lang.StringUtils;
import com.azure.cosmos.implementation.changefeed.common.ChangeFeedMode;
import com.azure.cosmos.implementation.changefeed.common.LeaseVersion;
import com.azure.cosmos.implementation.changefeed.pkversion.IncrementalChangeFeedProcessorImpl;
import com.azure.cosmos.implementation.changefeed.epkversion.FullFidelityChangeFeedProcessorImpl;
import com.azure.cosmos.models.ChangeFeedProcessorItem;
import com.azure.cosmos.models.ChangeFeedProcessorOptions;
import com.azure.cosmos.util.Beta;
import com.fasterxml.jackson.databind.JsonNode;

import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import static com.azure.cosmos.implementation.guava25.base.Preconditions.checkArgument;
import static com.azure.cosmos.implementation.guava25.base.Preconditions.checkNotNull;

/**
 * Helper class to build a {@link ChangeFeedProcessor} instance.
 *
 * Below is an example of building ChangeFeedProcessor for LatestVersion mode.
 *
 * 
 * 
 * ChangeFeedProcessor changeFeedProcessor = new ChangeFeedProcessorBuilder()
 *     .hostName(hostName)
 *     .feedContainer(feedContainer)
 *     .leaseContainer(leaseContainer)
 *     .handleChanges(docs -> {
 *         for (JsonNode item : docs) {
 *             // Implementation for handling and processing of each JsonNode item goes here
 *         }
 *     })
 *     .buildChangeFeedProcessor();
 * 
* * * Below is an example of building ChangeFeedProcessor with throughput control for handleChanges. * * *
 * ThroughputControlGroupConfig throughputControlGroupConfig =
 *         new ThroughputControlGroupConfigBuilder()
 *                 .groupName("cfp")
 *                 .targetThroughput(300)
 *                 .priorityLevel(PriorityLevel.LOW)
 *                 .build();
 * ChangeFeedProcessor changeFeedProcessor = new ChangeFeedProcessorBuilder()
 *         .hostName(hostName)
 *         .feedContainer(feedContainer)
 *         .leaseContainer(leaseContainer)
 *         .handleChanges(docs -> {
 *             for (JsonNode item : docs) {
 *                 // Implementation for handling and processing of each JsonNode item goes here
 *             }
 *         })
 *         .options(
 *                 new ChangeFeedProcessorOptions()
 *                         .setFeedPollThroughputControlConfig(throughputControlGroupConfig)
 *         )
 *         .buildChangeFeedProcessor();
 * 
* * * Below is an example of building ChangeFeedProcessor with throughput control for LatestVersion mode. * * *
 * ThroughputControlGroupConfig throughputControlGroupConfig =
 *         new ThroughputControlGroupConfigBuilder()
 *                 .groupName("cfp")
 *                 .targetThroughput(300)
 *                 .priorityLevel(PriorityLevel.LOW)
 *                 .build();
 * ChangeFeedProcessor changeFeedProcessor = new ChangeFeedProcessorBuilder()
 *         .hostName(hostName)
 *         .feedContainer(feedContainer)
 *         .leaseContainer(leaseContainer)
 *         .handleLatestVersionChanges(changeFeedProcessorItems -> {
 *             for (ChangeFeedProcessorItem item : changeFeedProcessorItems) {
 *                 // Implementation for handling and processing of each change feed item goes here
 *             }
 *         })
 *         .options(
 *                 new ChangeFeedProcessorOptions()
 *                         .setFeedPollThroughputControlConfig(throughputControlGroupConfig)
 *         )
 *         .buildChangeFeedProcessor();
 * 
* * * Below is an example of building ChangeFeedProcessor for AllVersionsAndDeletes mode. * * *
 * ChangeFeedProcessor changeFeedProcessor = new ChangeFeedProcessorBuilder()
 *     .hostName(hostName)
 *     .feedContainer(feedContainer)
 *     .leaseContainer(leaseContainer)
 *     .handleAllVersionsAndDeletesChanges(docs -> {
 *         for (ChangeFeedProcessorItem item : docs) {
 *             // Implementation for handling and processing of each ChangeFeedProcessorItem item goes here
 *         }
 *     })
 *     .buildChangeFeedProcessor();
 * 
* * * Below is an example of building ChangeFeedProcessor for AllVersionsAndDeletes mode when also wishing to process a {@link ChangeFeedProcessorContext}. * *
 * ChangeFeedProcessor changeFeedProcessor = new ChangeFeedProcessorBuilder()
 *     .hostName(hostName)
 *     .feedContainer(feedContainer)
 *     .leaseContainer(leaseContainer)
 *     .handleAllVersionsAndDeletesChanges((docs, context) -> {
 *         for (ChangeFeedProcessorItem item : docs) {
 *             // Implementation for handling and processing of each ChangeFeedProcessorItem item goes here
 *         }
 *         String leaseToken = context.getLeaseToken();
 *         // Handling of the lease token corresponding to a batch of change feed processor item goes here
 *     })
 *     .buildChangeFeedProcessor();
 * 
* */ public class ChangeFeedProcessorBuilder { private String hostName; private CosmosAsyncContainer feedContainer; private CosmosAsyncContainer leaseContainer; private ChangeFeedProcessorOptions changeFeedProcessorOptions; private Consumer> incrementalModeLeaseConsumerPkRangeIdVersion; private Consumer> incrementalModeLeaseConsumerEpkVersion; private Consumer> fullFidelityModeLeaseConsumer; private BiConsumer, ChangeFeedProcessorContext> fullFidelityModeLeaseWithContextConsumer; private ChangeFeedMode changeFeedMode = ChangeFeedMode.INCREMENTAL; private LeaseVersion leaseVersion = LeaseVersion.PARTITION_KEY_BASED_LEASE; /** * Instantiates a new Cosmos a new ChangeFeedProcessor builder. */ public ChangeFeedProcessorBuilder() { } /** * Sets the host name. * * @param hostName the name to be used for the host. When using multiple hosts, each host must have a unique * name. * @return current Builder. */ public ChangeFeedProcessorBuilder hostName(String hostName) { this.hostName = hostName; return this; } /** * Sets and existing {@link CosmosAsyncContainer} to be used to read from the monitored container. * * @param feedContainer the instance of {@link CosmosAsyncContainer} to be used. * @return current Builder. */ public ChangeFeedProcessorBuilder feedContainer(CosmosAsyncContainer feedContainer) { this.feedContainer = feedContainer; return this; } /** * Sets an existing {@link CosmosAsyncContainer} to be used to read from the leases container. * * @param leaseContainer the instance of {@link CosmosAsyncContainer} to use. * @return current Builder. */ public ChangeFeedProcessorBuilder leaseContainer(CosmosAsyncContainer leaseContainer) { this.leaseContainer = leaseContainer; return this; } /** * Sets a consumer function which will be called to process changes for LatestVersion change feed mode. * Attention! This API is not merge proof, please use {@link #handleLatestVersionChanges(Consumer)} instead. * * *
     * .handleChanges(docs -> {
     *     for (JsonNode item : docs) {
     *         // Implementation for handling and processing of each JsonNode item goes here
     *     }
     * })
     * 
* * * @param consumer the {@link Consumer} to call for handling the feeds. * @return current Builder. */ public ChangeFeedProcessorBuilder handleChanges(Consumer> consumer) { checkNotNull(consumer, "Argument 'consumer' can not be null"); checkArgument( this.incrementalModeLeaseConsumerEpkVersion == null, "handleLatestVersionChanges consumer has already been defined"); this.incrementalModeLeaseConsumerPkRangeIdVersion = consumer; this.changeFeedMode = ChangeFeedMode.INCREMENTAL; this.leaseVersion = LeaseVersion.PARTITION_KEY_BASED_LEASE; return this; } /** * Sets a consumer function which will be called to process changes for LatestVersion change feed mode. * * *
     * .handleLatestVersionChanges(changeFeedProcessorItems -> {
     *     for (ChangeFeedProcessorItem item : changeFeedProcessorItems) {
     *         // Implementation for handling and processing of each change feed item goes here
     *     }
     * })
     * 
* * * @param consumer the {@link Consumer} to call for handling the feeds. * @return current Builder. */ public ChangeFeedProcessorBuilder handleLatestVersionChanges(Consumer> consumer) { checkNotNull(consumer, "Argument 'consumer' can not be null"); checkArgument( this.incrementalModeLeaseConsumerPkRangeIdVersion == null, "handleChanges consumer has already been defined"); this.incrementalModeLeaseConsumerEpkVersion = consumer; this.changeFeedMode = ChangeFeedMode.INCREMENTAL; this.leaseVersion = LeaseVersion.EPK_RANGE_BASED_LEASE; return this; } /** * Sets a consumer function which will be called to process changes for AllVersionsAndDeletes change feed mode. * * *
     * .handleAllVersionsAndDeletesChanges(docs -> {
     *     for (ChangeFeedProcessorItem item : docs) {
     *         // Implementation for handling and processing of each ChangeFeedProcessorItem item goes here
     *     }
     * })
     * 
* * * @param consumer the {@link Consumer} to call for handling the feeds. * @return current Builder. */ @Beta(value = Beta.SinceVersion.V4_37_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) public ChangeFeedProcessorBuilder handleAllVersionsAndDeletesChanges(Consumer> consumer) { checkNotNull(consumer, "consumer cannot be null"); checkArgument(this.fullFidelityModeLeaseWithContextConsumer == null, "handleAllVersionsAndDeletesChanges biConsumer has already been defined."); this.fullFidelityModeLeaseConsumer = consumer; this.changeFeedMode = ChangeFeedMode.FULL_FIDELITY; this.leaseVersion = LeaseVersion.EPK_RANGE_BASED_LEASE; return this; } /** * Sets a {@link BiConsumer} function which will be called to process changes for AllVersionsAndDeletes change feed mode. * * *
     * .handleAllVersionsAndDeletesChanges((docs, context) -> {
     *     for (ChangeFeedProcessorItem item : docs) {
     *         // Implementation for handling and processing of each ChangeFeedProcessorItem item goes here
     *     }
     *     String leaseToken = context.getLeaseToken();
     *     // Handling of the lease token corresponding to a batch of change feed processor item goes here
     * })
     * 
* * * @param biConsumer the {@link BiConsumer} to call for handling the feeds and the {@link ChangeFeedProcessorContext} instance. * @return current Builder. */ @Beta(value = Beta.SinceVersion.V4_51_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING) public ChangeFeedProcessorBuilder handleAllVersionsAndDeletesChanges( BiConsumer, ChangeFeedProcessorContext> biConsumer) { checkNotNull(biConsumer, "biConsumer cannot be null"); checkArgument(this.fullFidelityModeLeaseConsumer == null, "handleAllVersionsAndDeletesChanges consumer has already been defined."); this.fullFidelityModeLeaseWithContextConsumer = biConsumer; this.changeFeedMode = ChangeFeedMode.FULL_FIDELITY; this.leaseVersion = LeaseVersion.EPK_RANGE_BASED_LEASE; return this; } /** * Sets the {@link ChangeFeedProcessorOptions} to be used. * Unless specifically set the default values that will be used are: *
    *
  • maximum items per page or FeedResponse: 100
  • *
  • lease renew interval: 17 seconds
  • *
  • lease acquire interval: 13 seconds
  • *
  • lease expiration interval: 60 seconds
  • *
  • feed poll delay: 5 seconds
  • *
  • maximum scale count: unlimited
  • *
* * @param changeFeedProcessorOptions the change feed processor options to use. * @return current Builder. */ public ChangeFeedProcessorBuilder options(ChangeFeedProcessorOptions changeFeedProcessorOptions) { this.changeFeedProcessorOptions = changeFeedProcessorOptions; return this; } /** * Builds a new instance of the {@link ChangeFeedProcessor} with the specified configuration. * * @return an instance of {@link ChangeFeedProcessor}. */ public ChangeFeedProcessor buildChangeFeedProcessor() { validateChangeFeedProcessorBuilder(); ChangeFeedProcessor changeFeedProcessor = null; // Lease version will decide which version of the changeFeed processor we are going to use internally // PARTITION_KEY_BASED_LEASE -> ChangeFeedProcessor pkversion // EPK_RANGE_BASED_LEASE -> ChangeFeedProcessor epkversion if (this.leaseVersion == LeaseVersion.EPK_RANGE_BASED_LEASE) { switch (this.changeFeedMode) { case FULL_FIDELITY: if (this.fullFidelityModeLeaseConsumer != null) { changeFeedProcessor = new FullFidelityChangeFeedProcessorImpl( this.hostName, this.feedContainer, this.leaseContainer, this.fullFidelityModeLeaseConsumer, this.changeFeedProcessorOptions); } else if (this.fullFidelityModeLeaseWithContextConsumer != null) { changeFeedProcessor = new FullFidelityChangeFeedProcessorImpl( this.hostName, this.feedContainer, this.leaseContainer, this.fullFidelityModeLeaseWithContextConsumer, this.changeFeedProcessorOptions); } break; case INCREMENTAL: changeFeedProcessor = new com.azure.cosmos.implementation.changefeed.epkversion.IncrementalChangeFeedProcessorImpl( this.hostName, this.feedContainer, this.leaseContainer, this.incrementalModeLeaseConsumerEpkVersion, this.changeFeedProcessorOptions); break; default: throw new IllegalStateException("ChangeFeed mode " + this.changeFeedMode + " is not supported"); } } else { changeFeedProcessor = new IncrementalChangeFeedProcessorImpl( this.hostName, this.feedContainer, this.leaseContainer, this.incrementalModeLeaseConsumerPkRangeIdVersion, this.changeFeedProcessorOptions); } return changeFeedProcessor; } private void validateChangeFeedProcessorBuilder() { checkArgument(StringUtils.isNotEmpty(hostName), "hostName cannot be null or empty"); checkNotNull(feedContainer, "Argument 'feedContainer' can not be null"); checkNotNull(leaseContainer, "Argument 'leaseContainer' can not be null"); if ((isIncrementalConsumerDefined() && isFullFidelityConsumerDefined()) || (!isIncrementalConsumerDefined() && !isFullFidelityConsumerDefined())) { throw new IllegalArgumentException("expecting either LatestVersion or AllVersionsAndDeletes consumer for handling change feed processor changes"); } validateChangeFeedProcessorOptions(); } private boolean isFullFidelityConsumerDefined() { return this.fullFidelityModeLeaseConsumer != null || this.fullFidelityModeLeaseWithContextConsumer != null; } private boolean isIncrementalConsumerDefined() { return this.incrementalModeLeaseConsumerEpkVersion != null || this.incrementalModeLeaseConsumerPkRangeIdVersion != null; } private void validateChangeFeedProcessorOptions() { if (this.changeFeedProcessorOptions == null) { return; } if (this.changeFeedProcessorOptions.getLeaseRenewInterval().compareTo(this.changeFeedProcessorOptions.getLeaseExpirationInterval()) >= 0) { // Lease renewer task must execute at a faster frequency than expiration setting; otherwise this will // force a lot of resets and lead to a poor overall performance of ChangeFeedProcessor. throw new IllegalArgumentException("changeFeedProcessorOptions: expecting leaseRenewInterval less than leaseExpirationInterval"); } // Some extra checks for all versions and deletes mode if (ChangeFeedMode.FULL_FIDELITY.equals(changeFeedMode)) { if (this.changeFeedProcessorOptions.getStartTime() != null) { throw new IllegalStateException("changeFeedProcessorOptions: AllVersionsAndDeletes change feed mode is not supported for startTime option."); } if (this.changeFeedProcessorOptions.isStartFromBeginning()) { throw new IllegalStateException("changeFeedProcessorOptions: AllVersionsAndDeletes change feed mode is not supported for startFromBeginning option."); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy