spring data redis cluster pipeline support - spring

Is there a plan to support "pipelined" operations for the spring data redis library when connecting to redis clustered version. There is considerable performance differences between pipelined & non-pipelined operations. If there is no such plan what are other viable options?

Spring Data Redis provides several RedisTemplate methods for executing commands in a pipeline. If you don’t care about the results of the pipelined operations, you can use the standard execute method, passing true for the pipeline argument. The executePipelined methods will execute the provided RedisCallback or SessionCallback in a pipeline and return the results. For example:
//pop a specified number of items from a queue
List<Object> results = stringRedisTemplate.executePipelined(
new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisConnection stringRedisConn = (StringRedisConnection)connection;
for(int i=0; i< batchSize; i++) {
stringRedisConn.rPop("myqueue");
}
return null;
}
});
The example above executes a bulk right pop of items from a queue in a pipeline. The results List contains all of the popped items. RedisTemplate uses its value, hash key, and hash value serializers to deserialize all results before returning, so the returned items in the above example will be Strings. There are additional executePipelined methods that allow you to pass a custom serializer for pipelined results.
Note that the value returned from the RedisCallback is required to be null, as this value is discarded in favor of returning the results of the pipelined commands.
Reference: http://docs.spring.io/spring-data/redis/docs/current/reference/html/#pipeline

Spring data redis does not supports the pipeline on cluster. So We can do it by our self in our application. If you use spring-data-redis and Jedis library.
So for that, we have to take Jedis Connection indirectly from Jedis Pool. Now if you only know keys of cluster then for that first you need to find out slot associated to key. You can get it by following way.
int slot = JedisClusterCRC16.getSlot(hKey);
Second, you can get JedisCluster connection by following way
JedisClusterConnection jedisClusterConnection = (JedisClusterConnection)stringRedisTemplate.getConnectionFactory().getClusterConnection();
JedisCluster jedisCluster = jedisClusterConnection.getNativeConnection();
Now you have JedisCluster connection but it is not enough to get Jedis connection from Jedis pool. Because JedisCluster not exposing connection handler directly and JedisSlotConnectionHandler classing having method which returns the jedis connection from slot. so for that we have to copy the BinaryJedisCluster class from package redis.clients.jedis into our application with same class and package name and you have to add following method to expose connection handler.
public JedisSlotBasedConnectionHandler getConnectionHandler() {
return (JedisSlotBasedConnectionHandler) this.connectionHandler;
}
Finally, you can able to get Jedis Connection by calling getJedisConnectionFromSlot(slot) method
JedisSlotBasedConnectionHandler jedisClusterConnectionHandler = jedisCluster.getConnectionHandler();
Jedis connection = jedisClusterConnectionHandler.getConnectionFromSlot(slot);
Pipeline pipeline = connection.pipelined();
pipeline.somecommand....
pipeline.sync();
Reference link

Related

In Spring Batch, linked with a ItemReader call I want to call a static util method to populate a string

I have a Spring Batch reader with following configurations.
This reader is reading from the database and and at a time its reading a page size records.
#Autowired
private SomeCreditRepot someCreditRepo;
public RepositoryItemReader<SomeCreditModel> reader() {
RepositoryItemReader<SomeCreditModel> reader = new RepositoryItemReader<>();
reader.setRepository(someCreditRepo);
reader.setMethodName("someCreditTransfer");
.
.
..
return reader;
}
I want to call utils method,
refValue = BatchProcessingUtil.generateSomeRefValue();
before the processor step, so that all the records fetched by the reader will have the same value set by which is given by the above call.
So that all the entity fetched by the reader will get the same value, in the processor.
And then this refValue will be written to another table StoreRefValue(table).
What is the right way to do this in Spring Batch?
Should I fire the query to write the refValue, to the table StoreRefValue in the processor?
You can let your processor implement the interface StepExecutionListener. You'll then have to implement the methods afterStep and beforeStep. The first should simply return null, and in beforeStep you can call the utility method and save its return value.
Alternatively, you can use the annotation #BeforeStep. If you use the usual Java DSL, it's not required to explicitly add the processor as a listener to the step. Adding it as a processor should suffice.
There are more details in the reference documentation:
https://docs.spring.io/spring-batch/docs/current/reference/html/step.html#interceptingStepExecution

java 8 parallel stream with ForkJoinPool and ThreadLocal

We are using java 8 parallel stream to process a task, and we are submitting the task through ForkJoinPool#submit. We are not using jvm wide ForkJoinPool.commonPool, instead we are creating our own custom pool to specify the parallelism and storing it as static variable.
We have validation framework, where we subject a list of tables to a List of Validators, and we submit this job through the custom ForkJoinPool as follows:
static ForkJoinPool forkJoinPool = new ForkJoinPool(4);
List<Table> tables = tableDAO.findAll();
ModelValidator<Table, ValidationResult> validator = ValidatorFactory
.getInstance().getTableValidator();
List<ValidationResult> result = forkJoinPool.submit(
() -> tables.stream()
.parallel()
.map(validator)
.filter(result -> result.getValidationMessages().size() > 0)
.collect(Collectors.toList())).get();
The problem we are having is, in the downstream components, the individual validators which run on separate threads from our static ForkJoinPool rely on tenant_id, which is different for every request and is stored in an InheritableThreadLocal variable. Since we are creating a static ForkJoinPool, the threads pooled by the ForkJoinPool will only inherit the value of the parent thread, when it is created first time. But these pooled threads will not know the new tenant_id for the current request. So for subsequent execution these pooled threads are using old tenant_id.
I tried creating a custom ForkJoinPool and specifying ForkJoinWorkerThreadFactory in the constructor and overriding the onStart method to feed the new tenant_id. But that doesnt work, since the onStart method is called only once at creation time and not during individual execution time.
Seems like we need something like the ThreadPoolExecutor#beforeExecute which is not available in case of ForkJoinPool. So what alternative do we have if we want to pass the current thread local value to the statically pooled threads?
One workaround would be to create the ForkJoinPool for each request, rather than make it static but we wouldn't want to do it, to avoid the expensive nature of thread creation.
What alternatives do we have?
I found the following solution that works without changing any underlying code. Basically, the map method takes a functional interface which I am representing as a lambda expression. This expression adds a preExecution hook to set the new tenantId in the current ThreadLocal and cleaning it up in postExecution.
forkJoinPool.submit(tables.stream()
.parallel()
.map((item) -> {
preExecution(tenantId);
try {
return validator.apply(item);
} finally {
postExecution();
}
}
)
.filter(validationResult ->
validationResult.getValidationMessages()
.size() > 0)
.collect(Collectors.toList())).get();
The best option in my view would be to get rid of the thread local and pass it as an argument instead. I understand that this could be a massive undertaking though. Another option would be to use a wrapper.
Assuming that your validator has a validate method you could do something like:
public class WrappingModelValidator implements ModelValidator<Table. ValidationResult> {
private final ModelValidator<Table. ValidationResult> v;
private final String tenantId;
public WrappingModelValidator(ModelValidator<Table. ValidationResult> v, String tenantId) {
this.v = v;
this.tenantId = tenantId;
}
public ValidationResult validate(Table t) {
String oldValue = YourThreadLocal.get();
YourThreadLocal.set(tenantId);
try {
return v.validate(t);
} finally {
YourThreadLocal.set(oldValue);
}
}
}
Then you simply wrap your old validator and it will set the thread local on entry and restore it when done.

How to Access attributes from grpc Context.current()?

Would it be possible to access context attributes of a grpc call from the rpc method definition?
I have written a Server Interceptor which is something similar to this
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, final Metadata requestHeaders, ServerCallHandler<ReqT, RespT> next) {
Context.Key<String> USER = Context.key("USER");
Context ctx = Context.current().withValue(USER, "chandan");
return Contexts.interceptCall(ctx, call, requestHeaders, next);
}
and in the service implementation, I was trying something like
Context.Key<String> key = Context.key("USER");
String value = key.get(Context.current())
Everytime value was null. However While debugging in intellij I could see those values in the context.current().keyValueEntries as
CompressedIndex(bitmap=100001000000000000000000000000 Leaf(key=USER value=chandan) Leaf(key=opencensus-trace-span-key value=BlankSpan) )
How do I access the Context attributes/What is the right way to go about this?
Context.Key uses reference equality. The "USER" string is a debug string used in toString() output. You should create the Key once and reference that one instance anywhere you need it.
Because Context.Key uses reference equality, you can use normal Java visibility restrictions to limit who can access the value, just like you could do with ThreadLocal.

Dynamically initialize multiple data sources in Spring

In my spring application , I need to dynamically initialize multiple data sources based on some values set up in the application configuration.
I am aware of the AbstractRoutingDataSource class provided by spring jdbc library but it helps only when you need to initialize a single data source based on a single look up key value at a time.
Is it possible to extend the AbstractRoutingDataSource and change its behavior to support multiple key look up and data source resolution? Is there any other alternative approach ? Reference
Basically I am trying to achieve something like this through AbstractDataSourceRouter class:
public class DataSourceRouter extends AbstractRoutingDataSource {
#Value("${com.listdb.datasource.switch}")
private short listDBSwitch;
#Value("${com.scoringdb.datasource.switch}")
private short scoringDbSwitch;
#Value("${com.configmaster.datasource.switch}")
private short configDbSwitch;
private List<String> configuredDataSources;
/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
* <p>Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {#link #resolveSpecifiedLookupKey} method.
*/
#Override
protected Object determineCurrentLookupKey() {
if(ListUtil.isListNotEmpty(configuredDataSources)) {
configuredDataSources =new ArrayList<String>();
String listDBString = (listDBSwitch == 1)?DataSources.LIST.toString() : null;
String configDBString = (configDbSwitch == 1) ? DataSources.CONFIGMASTER.toString() :null;
String scoringDBString = (scoringDbSwitch == 1) ? DataSources.SCORING.toString() : null;
/**
* Add all configured data source keys for look up
*/
configuredDataSources.add(listDBString);
configuredDataSources.add(configDBString);
configuredDataSources.add(scoringDBString);
}
return configuredDataSources;
}
}
Any help/suggestions?
This is not really possible with current spring/hibernate versions even if it would be neat to have it that way. If you need multiple data sources and use AbstractRoutingDataSource to achieve this, then one possible solution is to let spring initialize one DB (the default/configuration DB) and add e.g. init.sql script (or flyway/liquibase such if you are more into that) that initializes all other under the same AbstractRoutingDataSource.
This approach works nicely and gives you more control over your (hopefully test!) environment. Personally I like to have more control over DB schema then any auto-initializers can provide, however that's only a taste/style issue.

Test Hector Spring Cassandra connection

In my Java-Spring based web app I'm connecting to Cassandra DB using Hector and Spring.
The connection works just fine but I would like to be able to test the connection.
So if I intentionally provide a wrong host to CassandraHostConfigurator I get an error:
ERROR connection.HConnectionManager: Could not start connection pool for host <myhost:myport>
Which is ok of course. But how can I test this connection?
If I define the connection pragmatically (and not via spring context) it is clear, but via spring context it is not really clear how to test it.
can you think of an idea?
Since I could not come up nor find a satisfying answer I decided to define my connection pragmatically and to use a simple query:
private ColumnFamilyResult<String, String> readFromDb(Keyspace keyspace) {
ColumnFamilyTemplate<String, String> template = new ThriftColumnFamilyTemplate<String, String>(keyspace, tableName, StringSerializer.get(),
StringSerializer.get());
// It doesn't matter if the column actually exists or not since we only check the
// connection. In case of connection failure an exception is thrown,
// else something comes back.
return template.queryColumns("some_column");
}
And my test checks that the returned object in not null.
Another way that works fine:
public boolean isConnected() {
List<KeyspaceDefinition> keyspaces = null;
try {
keyspaces = cluster.describeKeyspaces();
} catch (HectorException e) {
return false;
}
return (!CollectionUtils.isEmpty(keyspaces));
}

Resources