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

io.split.engine.segments.SegmentFetcherImp Maven / Gradle / Ivy

package io.split.engine.segments;

import io.split.client.dtos.SegmentChange;
import io.split.storages.SegmentCacheProducer;
import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
import io.split.engine.common.FetchOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

import static com.google.common.base.Preconditions.checkNotNull;

public class SegmentFetcherImp implements SegmentFetcher {
    private static final Logger _log = LoggerFactory.getLogger(SegmentFetcherImp.class);

    private final String _segmentName;
    private final SegmentChangeFetcher _segmentChangeFetcher;
    private final SegmentCacheProducer _segmentCacheProducer;
    private final TelemetryRuntimeProducer _telemetryRuntimeProducer;

    private final Object _lock = new Object();

    public SegmentFetcherImp(String segmentName, SegmentChangeFetcher segmentChangeFetcher, SegmentCacheProducer segmentCacheProducer,
                             TelemetryRuntimeProducer telemetryRuntimeProducer) {
        _segmentName = checkNotNull(segmentName);
        _segmentChangeFetcher = checkNotNull(segmentChangeFetcher);
        _segmentCacheProducer = checkNotNull(segmentCacheProducer);
        _telemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);

        _segmentCacheProducer.updateSegment(segmentName, new ArrayList<>(), new ArrayList<>(), -1L);
    }

    @Override
    public boolean fetch(FetchOptions opts){
        try {
            final long INITIAL_CN = _segmentCacheProducer.getChangeNumber(_segmentName);
            while (true) {
                long start = _segmentCacheProducer.getChangeNumber(_segmentName);
                runWithoutExceptionHandling(opts);
                if (INITIAL_CN == start) {
                    opts = new FetchOptions.Builder(opts).targetChangeNumber(FetchOptions.DEFAULT_TARGET_CHANGENUMBER).build();
                }
                long end = _segmentCacheProducer.getChangeNumber(_segmentName);
                if (start >= end) {
                    break;
                }
            }
            return true;
        } catch (Exception e){
            _log.error("RefreshableSegmentFetcher failed: " + e.getMessage());
            if (_log.isDebugEnabled()) {
                _log.debug("Reason:", e);
            }
            return false;
        }
    }

    private void runWithoutExceptionHandling(FetchOptions options) {
        if (_log.isDebugEnabled()) {
            _log.debug(String.format("Synchronizing segment %s", _segmentName));
        }
        SegmentChange change = _segmentChangeFetcher.fetch(_segmentName, _segmentCacheProducer.getChangeNumber(_segmentName), options);

        if (change == null) {
            throw new IllegalStateException("SegmentChange was null");
        }

        if (change.since != _segmentCacheProducer.getChangeNumber(_segmentName)
                || change.since < _segmentCacheProducer.getChangeNumber(_segmentName)) {
            // some other thread may have updated the shared state. exit
            return;
        }

        if (change.added.isEmpty() && change.removed.isEmpty()) {
            // there are no changes. weird!
            _segmentCacheProducer.setChangeNumber(_segmentName,change.till);
            return;
        }

        synchronized (_lock) {
            // check state one more time.
            if (change.since != _segmentCacheProducer.getChangeNumber(_segmentName)
                    || change.till < _segmentCacheProducer.getChangeNumber(_segmentName)) {
                // some other thread may have updated the shared state. exit
                return;
            }
            _segmentCacheProducer.updateSegment(_segmentName,change.added, change.removed, change.till);

            if (!change.added.isEmpty()) {
                _log.info(_segmentName + " added keys: " + summarize(change.added));
            }

            if (!change.removed.isEmpty()) {
                _log.info(_segmentName + " removed keys: " + summarize(change.removed));
            }

            _telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.SEGMENTS, System.currentTimeMillis());
        }
    }

    private String summarize(List changes) {
        StringBuilder bldr = new StringBuilder();
        bldr.append("[");
        for (int i = 0; i < Math.min(3, changes.size()); i++) {
            if (i != 0) {
                bldr.append(", ");
            }
            bldr.append(changes.get(i));
        }

        if (changes.size() > 3) {
            bldr.append("... ");
            bldr.append((changes.size() - 3));
            bldr.append(" others");
        }
        bldr.append("]");

        return bldr.toString();
    }

    @Override
    public boolean runWhitCacheHeader(){
       return this.fetch(new FetchOptions.Builder().cacheControlHeaders(true).build());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy