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

org.occurrent.subscription.blocking.durable.catchup.CatchupSubscriptionModelConfig Maven / Gradle / Ivy

There is a newer version: 0.19.5
Show newest version
/*
 * Copyright 2020 Johan Haleby
 *
 * 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 org.occurrent.subscription.blocking.durable.catchup;

import org.occurrent.eventstore.api.SortBy;
import org.occurrent.eventstore.api.blocking.EventStoreQueries;
import org.occurrent.subscription.api.blocking.Subscription;

import java.util.Objects;

import static org.occurrent.cloudevents.OccurrentCloudEventExtension.STREAM_VERSION;
import static org.occurrent.filter.Filter.TIME;

/**
 * Configuration for {@link CatchupSubscriptionModel}
 */
public class CatchupSubscriptionModelConfig {

    public final int cacheSize;
    public final SubscriptionPositionStorageConfig subscriptionStorageConfig;
    public final SortBy catchupPhaseSortBy;

    /**
     * Create a new {@code CatchupSubscriptionModelConfig} will the given cache size. Will default to sort by time and then stream version (if time is the same for two events)
     * during the catchup phase. You can change this by calling {@link #catchupPhaseSortBy(SortBy)}.
     *
     * @param cacheSize The number of cloud events id's to store in-memory when switching from "catch-up" mode (i.e. querying the {@link EventStoreQueries} API)
     *                  and "subscription" mode ({@link Subscription}). The cache is needed to reduce the number of duplicate events the occurs when switching.
     */
    public CatchupSubscriptionModelConfig(int cacheSize) {
        this(cacheSize, SubscriptionPositionStorageConfig.dontUseSubscriptionPositionStorage());
    }

    /**
     * Create a new {@code CatchupSubscriptionModelConfig} will the given subscription storage config. Will default to sort by time and then stream version (if time is the same for two events)
     * during the catchup phase. You can change this by calling {@link #catchupPhaseSortBy(SortBy)}.
     *
     * @param subscriptionStorageConfig Configures if and how subscription position persistence should be handled during the catch-up phase.
     */
    public CatchupSubscriptionModelConfig(SubscriptionPositionStorageConfig subscriptionStorageConfig) {
        this(100, subscriptionStorageConfig);
    }

    /**
     * Create a new {@code CatchupSubscriptionModelConfig} will the given settings. Will default to sort by time and then stream version (if time is the same for two events)
     * during the catchup phase. You can change this by calling {@link #catchupPhaseSortBy(SortBy)}.
     *
     * @param cacheSize                 The number of cloud events id's to store in-memory when switching from "catch-up" mode (i.e. querying the {@link EventStoreQueries} API)
     *                                  and "subscription" mode ({@link Subscription}). The cache is needed to reduce the number of duplicate events the occurs when switching.
     * @param subscriptionStorageConfig Configures if and how subscription position persistence should be handled during the catch-up phase.
     */
    public CatchupSubscriptionModelConfig(int cacheSize, SubscriptionPositionStorageConfig subscriptionStorageConfig) {
        // We sort by time but fallback to stream version if time is the same for two events.
        // While this will _not_ sort the entire in database in insertion order, it at least guarantees
        // order within a stream. Note that we can't do SortBy.time(ASCENDING).thenNatural(ASCENDING)
        // since for certain databases (MongoDB) this will prevent sorting from using a "time index" for queries
        // (see https://docs.mongodb.com/manual/reference/method/cursor.sort/#return-natural-order).
        // For MongoDB, doing SortBy.time(ASCENDING).then("_id", ASCENDING) would be better,
        // but "_id" is unique to MongoDB so we cannot use it here.
        this(cacheSize, subscriptionStorageConfig, SortBy.ascending(TIME, STREAM_VERSION));
    }

    private CatchupSubscriptionModelConfig(int cacheSize, SubscriptionPositionStorageConfig subscriptionStorageConfig, SortBy sortBy) {
        if (cacheSize < 1) {
            throw new IllegalArgumentException("Cache size must be greater than or equal to 1");
        }
        Objects.requireNonNull(subscriptionStorageConfig, SubscriptionPositionStorageConfig.class.getSimpleName() + " cannot be null");
        Objects.requireNonNull(sortBy, SortBy.class + " cannot be null");
        this.cacheSize = cacheSize;
        this.subscriptionStorageConfig = subscriptionStorageConfig;
        this.catchupPhaseSortBy = sortBy;
    }

    /**
     * Specify how to sort the events that are read from the event store during catch-up phase. By default, "natural order" is used when
     * no filter is specified, and time then stream version, if time is the same for two events. If you know that you're reading from a datastore
     * that has insertion order support, or if you need a different sort events after they've been filtered by the {@link CatchupSubscriptionModel},
     * you can specify your own {@code sortBy} instance here. Note that you most likely need the {@code sortBy} instance be covered by an index for
     * it to work efficiently.
     * 

* For example, in MongoDB, if you only sort by "time", then if two events have the exact same time, then the order returned from MongoDB is unspecified. * Thus the default value of {@code sortBy} is SortBy.ascending(TIME, STREAM_VERSION). However, say that your filter is * Filter.type(""), then you could create an index, {type : 1, time : 1, _id : 1}, and call {@link #catchupPhaseSortBy(SortBy)} * with SortBy.ascending(TIME, "_id"). This means that MongoDB can efficiently both search for the correct type and then perform a sort based on time, * but use "insertion order" if time is the same for two or more events. If you don't supply a filter, then you can instead create the index {time : 1, _id : 1}. *

* * @param sortBy The {@link SortBy} instance to use during catchup phase. Default is SortBy.ascending(TIME, STREAM_VERSION). * @return A new instance of {@link CatchupSubscriptionModel}. */ public CatchupSubscriptionModelConfig catchupPhaseSortBy(SortBy sortBy) { return new CatchupSubscriptionModelConfig(cacheSize, subscriptionStorageConfig, sortBy); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CatchupSubscriptionModelConfig)) return false; CatchupSubscriptionModelConfig that = (CatchupSubscriptionModelConfig) o; return cacheSize == that.cacheSize && Objects.equals(subscriptionStorageConfig, that.subscriptionStorageConfig); } @Override public int hashCode() { return Objects.hash(cacheSize, subscriptionStorageConfig); } @Override public String toString() { return "CatchupSupportingBlockingSubscriptionConfig{" + "cacheSize=" + cacheSize + ", catchupPositionPersistenceConfig=" + subscriptionStorageConfig + '}'; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy