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

com.redislabs.riot.redis.RedisItemReader Maven / Gradle / Ivy


package com.redislabs.riot.redis;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.support.AbstractItemStreamItemReader;
import org.springframework.util.ClassUtils;

import io.lettuce.core.api.StatefulConnection;
import io.lettuce.core.api.async.BaseRedisAsyncCommands;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RedisItemReader extends AbstractItemStreamItemReader {

	private KeyIterator keyIterator;
	private GenericObjectPool> pool;
	private Function, BaseRedisAsyncCommands> asyncApi;
	private int queueCapacity;
	private int threads;
	private int pipeline;
	private Long flushRate;
	private ValueReader reader;

	private Object lock = new Object();
	private BlockingQueue queue;
	private ExecutorService executor;
	private ScheduledExecutorService scheduler;
	private List> producers;

	@Builder
	protected RedisItemReader(KeyIterator keyIterator, GenericObjectPool> pool,
			Function, BaseRedisAsyncCommands> asyncApi,
			int queueCapacity, int threads, int pipeline, ValueReader reader, Long flushRate) {
		setName(ClassUtils.getShortName(RedisItemReader.class));
		this.keyIterator = keyIterator;
		this.pool = pool;
		this.asyncApi = asyncApi;
		this.queueCapacity = queueCapacity;
		this.threads = threads;
		this.pipeline = pipeline;
		this.reader = reader;
		this.flushRate = flushRate;
	}

	@Override
	public void open(ExecutionContext executionContext) {
		synchronized (lock) {
			if (queue != null) {
				return;
			}
			log.debug("Starting key iterator");
			keyIterator.start();
			log.debug("Creating queue with capacity {}", queueCapacity);
			queue = new LinkedBlockingDeque<>(queueCapacity);
			log.debug("Creating thread pool of size {}", threads);
			executor = Executors.newFixedThreadPool(threads);
			scheduler = Executors.newSingleThreadScheduledExecutor();
			producers = new ArrayList<>(threads);
			for (int index = 0; index < threads; index++) {
				log.debug("Adding KeyValue producer");
				producers.add(ValueProducer.builder().keyIterator(keyIterator).pipeline(pipeline).pool(pool)
						.asyncApi(asyncApi).queue(queue).reader(reader).build());
			}
			for (ValueProducer producer : producers) {
				log.debug("Starting producer");
				executor.submit(producer);
				if (flushRate != null) {
					scheduler.scheduleAtFixedRate(producer::flush, flushRate, flushRate, TimeUnit.MILLISECONDS);
				}
			}
		}
	}

	@Override
	public void close() {
		synchronized (lock) {
			producers.forEach(p -> p.stop());
			scheduler.shutdown();
			scheduler = null;
			executor.shutdown();
			executor = null;
			queue = null;
			keyIterator.stop();
		}
	}

	@Override
	public T read() {
		T value;
		do {
			try {
				value = queue.poll(100, TimeUnit.MILLISECONDS);
			} catch (InterruptedException e) {
				return null;
			}
		} while (value == null && keyIterator.hasNext());
		return value;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy