I have an inventory list of Millions of records that I want to insert/merge in batches in Redis using Redisson Batch command.
below is the code
public void upsertInventoryInBatches(final List<ItemInventory> itemInventory) throws ExecutionException, InterruptedException {
RBatch batch = redissonClient.createBatch(BatchOptions.defaults().responseTimeout(300, TimeUnit.SECONDS));
RMapAsync<String, ItemInventory> map = batch.getMap(IMSConstant.REDIS_INVENTORY_MAP);
try {
for (ItemInventory item : itemInventory) {
map.mergeAsync(item.getKey(), item, (existing, newValue) -> {
if (existing == null) {
return newValue;
} else {
if (existing.getQuantity() == newValue.getQuantity()
&& existing.getMinMRP() == newValue.getMinMRP()) {
return existing;
}
existing.setQuantity(item.getQuantity());
existing.setMinMRP(item.getMinMRP());
existing.setEarliestExpiryDate(item.getEarliestExpiryDate());
existing.setVersion(item.getVersion());
return existing;
}
});
}
var res = batch.execute(); // Hangs with no result and no error
} catch (Exception e) {
System.out.println(e.getMessage());
}
thebatch.execute statement just hangs with no error and no output.
Looking for guidance on what I am doing wrong.
batch.getMap(IMSConstant.REDIS_INVENTORY_MAP).putAsync(item.getKey(), item) works fine but I want to merge the values. if its not possible with redisson, Is it possible via any redis java client?
Related
My situation
I'm trying to craft a functionality which would execute n (where n >=0) requests to a given endpoint, but I do understand that sometimes that endpoint might not respond due to
500 error or other issue, so I want to repeat my requests to an endpoint (with a
small interval in between [not yet implemented]) till I get a response, or till I get an unknown error which would indicate what I can't repeat, because of other reasons than a crashed server.
So, I've tried to implement this piece of functionality using Executors and concurrency provided by Java 11 and it does not work as I want
I can't resubmit failed tasks till I get all the responses and I don't know why
I have a method
private void DoMyTasks(List<MyRequest> requests) {
final ExecutorService executorService = Executors.newFixedThreadPool(10);
final ExecutorCompletionService<MyReqResDto> completionService =
new ExecutorCompletionService<>(executorService);
for (final MyRequest MyRequest : requests) {
completionService.submit(new MyCallableRequest(webClient, MyRequest));
}
List<MyReqResDto> responses = new ArrayList<>();
for (int i = 0; i < requests.size(); ++i) {
try {
final Future<MyReqResDto> future = completionService.take();
if (future.get().getEx() != null) {
completionService.submit(new MyCallableRequest(webClient, future.get().getMyRequest()));
}
responses.add(future.get());
} catch (ExecutionException | InterruptedException e) {
log.warn("Error"));
} catch (Exception exception) {
log.error("Other error");
} finally {
executorService.shutdown();
try {
if (!executorService.awaitTermination(10, TimeUnit.MINUTES)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
}
}
responses.size();
}
I'm trying to repeat failed tasks with
if (future.get().getEx() != null) {
completionService.submit(new MyCallableRequest(webClient, future.get().getMyRequest()));
}
and yet, at the end of execution I don't get all responses for my requests. What I get is at most 3 to 5 responses when I try executing 10 requests. Why? How to fix it?
My callable class is
public class MyCallableRequest implements Callable<MyReqResDto> {
private final WebClient webClient;
private final MyRequest myRequest;
public MyCallableRequest(WebClient webClient, MyRequest myRequest) {
this.webClient = webClient;
this.myRequest = myRequest;
}
#Override
public MyReqResDto call() throws Exception {
try {
if (new Random().nextInt(10) % 2 == 0) {
throw new TestException();
}
if (new Random().nextInt(10) % 7 == 0) {
throw new RuntimeException();
}
WebClient.UriSpec<WebClient.RequestBodySpec> uriSpec = webClient.post();
WebClient.RequestBodySpec bodySpec = uriSpec.uri(
s -> s.path("/myEndpoint").build());
MyRequestDto myMyRequestDto = new MyRequestDto();
WebClient.RequestHeadersSpec<?> headersSpec =
bodySpec.body(Mono.just(myMyRequestDto), MyRequestDto.class);
ResponseDto responseDto = headersSpec.exchangeToMono(s -> {
if (s.statusCode().equals(HttpStatus.OK)) {
return s.bodyToMono(ResponseDto.class);
} else if (s.statusCode().is1xxInformational()) {
return s.createException().flatMap(Mono::error);
} else if (s.statusCode().is3xxRedirection()) {
return s.createException().flatMap(Mono::error);
} else if (s.statusCode().is4xxClientError()) {
return s.createException().flatMap(Mono::error);
} else if (s.statusCode().is5xxServerError()) {
return s.createException().flatMap(Mono::error);
} else {
return s.createException().flatMap(Mono::error);
}
//return null;
}).block();
return new MyReqResDto(myRequest, responseDto, null);
} catch (Exception exception) {
return new MyReqResDto(myRequest, null, exception);
}
}
}
Update NO. 1
I changed a for loop to a while loop according to a comment provided by
Slaw and an answer provided by erickson. And this solutions works, meaning that
it is hammering an endpoint till all responses are received without
any errors. But I'm still not sure it feels that I'm building a sh**
tower with this solution. Is there any thread related issues that I should be aware while using executor like this?
while (true) {
Future < MyReqResDto > future = null;
try {
future = completionService.take();
if (future.get().getEx() != null /*and check exception if possible to handle, if not break from a loop*/) {
completionService.submit(new MyCallableRequest(webClient, future.get().getRequestCT());
} else {
responseDtos.add(future.get());
}
} catch (ExecutionException | InterruptedException e) {
log.warn("Error while downloading", e.getCause());
// test if I can recover from these exceptions if no
break;
}
}
if (responseDtos.size() == requests.size()) {
executorService.shutdown();
try {
if (!executorService.awaitTermination(10, TimeUnit.MINUTES)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
break;
}
You are shutting down the executor as soon as you get one response. Perhaps a few more have completed in this time, but you are not allowing time for any others to complete.
Your logic here is wrong. The executor should only be shut down when you are sure no more tasks will be submitted; at soonest, that is after the loop responsible for re-submitting failures.
Here is a simplified view of your code to highlight the premature shutdown:
for (int i = 0; i < requests.size(); ++i) {
try {
final Future<MyReqResDto> future = completionService.take();
...
responses.add(future.get());
...
} finally {
executorService.shutdown();
}
}
Having a hard time to do a reliable retry of a background task which sends request to let's say mail service in order to get latest emails. Once emails successfully received the execution should continue in thenAccept() block - persist emails, however if exception occurs I have to rerun mail retrieval until successful attempt and on success should persist mails and stop. Please take a look and advice if I do it wrong.
private void retrieveMailsAsync(User user) {
CompletableFuture.supplyAsync(() -> {
try {
return mailService.getEmails(user.getName(), user.getPassword());
} catch (InvalidAuthentication | TimeoutException | BadGatewayException e) {
throw new CompletionException(e);
}
}).thenAccept(email -> {
mailService.persist(email);
}).exceptionally(ex -> {
log.log(Level.SEVERE, "Exception retrieveMailsAsync emails, Retrying retrieveMailsAsync:: ", ex.getCause());
retrieveMailsAsync(user);
return null;
});
}
P.S please also take a look at how I'm handling checked exception wrapping it into CompletionException and rethrowing - the main idea here to handle all exceptions (defined checked and runtime) in one exceptionally() block rather than logging them in catch block and return null.
Thanks guys in advance, hope I'm not doing pretty stupid stuff, or at least there is already reliable solutions exists for Java 8.
What I meant in my comment was this:
private void retrieveMailsAsync(User user) {
CompletableFuture.supplyAsync(() -> {
while (continueQuery()) { // true for infinite retries, or some other logic
try {
return mailService.getEmails(user.getName(), user.getPassword());
} catch (InvalidAuthentication | TimeoutException | BadGatewayException e) {
log.log(Level.SEVERE, "Exception retrieveMailsAsync emails, Retrying retrieveMailsAsync: ", e);
}
}
return null;
}).thenAccept(email -> {
mailService.persist(email);
});
}
Ie you just retry in the submitted runnable until you don't get an exception anymore.
I think you can achieve that via :
public static void main(String[] args) {
String result = call(new User().setName("name").setPassword("p")).join();
System.out.println(result);
}
private static CompletableFuture<String> call(User user) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> retrieveMailsAsync(user));
return future.handleAsync((String result, Throwable ex) -> {
// or any other Predicate that is satisfied against ex
if(ex != null) {
return call(user);
} else {
return future;
}
}).thenCompose(Function.identity());
}
EDIT
So what stays in your way to change the code above, for example, to:
static ExecutorService service = Executors.newFixedThreadPool(1);
public static void main(String[] args) {
call(new User().setName("name").setPassword("p"))
// chain any other action here, like mailService.persist(email);
.thenAcceptAsync(
System.out::println,
service
);
System.out.println("Continue main thread");
}
private static CompletableFuture<String> call(User user) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> retrieveMailsAsync(user), service);
return future.handleAsync((String result, Throwable ex) -> {
// or any other Predicate that is satisfied against ex
if(ex != null) {
return call(user);
} else {
return future;
}
}).thenCompose(Function.identity());
}
I have been trying to implement a DAO method for delete operation for Azure Storage entities. Delete using TableOperation was ok.
TableOperation deleteEntity = TableOperation.delete(entity);
But when I tried it using Batch Operation, It was not supported.
Any suggestions to overcome this issue is highly appreciated.
But when I tried it using Batch Operation, It was not supported.
I assumed that you could group your items for deleting by partition key, then execute the TableBatchOperation.
Here I wrote a helper class via C# language for achieving this purpose, you could refer to it:
public class TableBatchHelper<T> where T : ITableEntity
{
const int batchMaxSize = 100;
public static IEnumerable<TableBatchOperation> GetBatchesForDelete(IEnumerable<T> items)
{
var list = new List<TableBatchOperation>();
var partitionGroups = items.GroupBy(arg => arg.PartitionKey).ToArray();
foreach (var group in partitionGroups)
{
T[] groupList = group.ToArray();
int offSet = batchMaxSize;
T[] entities = groupList.Take(offSet).ToArray();
while (entities.Any())
{
var tableBatchOperation = new TableBatchOperation();
foreach (var entity in entities)
{
tableBatchOperation.Add(TableOperation.Delete(entity));
}
list.Add(tableBatchOperation);
entities = groupList.Skip(offSet).Take(batchMaxSize).ToArray();
offSet += batchMaxSize;
}
}
return list;
}
public static async Task BatchDeleteAsync(CloudTable table, IEnumerable<T> items)
{
var batches = GetBatchesForDelete(items);
await Task.WhenAll(batches.Select(table.ExecuteBatchAsync));
}
}
Then, you could you execute the batch deleting as follows:
await TableBatchHelper<ClassName>.BatchDeleteAsync(cloudTable,items);
Or
var batches = TableBatchHelper<ClassName>.GetBatchesForDelete(entities);
Parallel.ForEach(batches, new ParallelOptions()
{
MaxDegreeOfParallelism = 5
}, (batchOperation) =>
{
try
{
table.ExecuteBatch(batchOperation);
Console.WriteLine("Writing {0} records", batchOperation.Count);
}
catch (Exception ex)
{
Console.WriteLine("ExecuteBatch throw a exception:" + ex.Message);
}
});
No, That was the code without using block operation. Following is the code that includes block operation. Sorry for not mentioning that
TableBatchOperation batchOperation = new TableBatchOperation();
List<TableBatchOperation> list = new ArrayList<>();
if (partitionQuery != null) {
for (AzureLocationData entity : cloudTable.execute(partitionQuery)) {
batchOperation.add(TableOperation.delete(entity));
list.add(batchOperation); //exception thrown line
}
try {
cloudTable.execute((TableOperation) batchOperation);
} catch (StorageException e) {
e.printStackTrace();
}
}
public void deleteLocationsForDevice(String id) {
logger.info("Going to delete location data for Device [{}]", id);
// Create a filter condition where the partition key is deviceId.
String partitionFilter = TableQuery.generateFilterCondition(
PARTITION_KEY,
TableQuery.QueryComparisons.EQUAL,
id);
// Specify a partition query, using partition key filter.
TableQuery<AzureLocationData> partitionQuery =
TableQuery.from(AzureLocationData.class)
.where(partitionFilter);
if (partitionQuery != null) {
for (AzureLocationData entity : cloudTable.execute(partitionQuery)) {
TableOperation deleteEntity = TableOperation.delete(entity);
try {
cloudTable.execute(deleteEntity);
logger.info("Successfully deleted location records with : " + entity.getPartitionKey());
} catch (StorageException e) {
e.printStackTrace();
}
}
} else {
logger.debug("No records to delete!");
}
// throw new UnsupportedOperationException("AzureIotLocationDataDao Delete Operation not supported");
}
I am using redis with java using Jedis as redis-client. I have a class
public class RedisDBManager
which has methods which call Jedis to execute the commands on redis.
Sample method inside RedisDBManager
public Set<NTuple> zrangeWithScores(String key, int min, int max)
{
JedisSentinelPool localPool = redisSentinelPool;
Jedis redis = null;
try
{
redis = getResource();
Set<Tuple> tupleSet = redis.zrangeWithScores(key, min, max);
Set<NTuple> tuples = new LinkedHashSet<>();
for (Tuple tuple : tupleSet) {
tuples.add(new NTuple(tuple.getElement(), tuple.getScore()));
}
return tuples;
}
catch (JedisConnectionException jex) {
logger.error("Creating new connection since it encountered Jedis Connection Exception: ",jex);
createNewConnectionPool(localPool);
try {
redis = getResource();
Set<Tuple> tupleSet = redis.zrangeWithScores(key, min, max);
Set<NTuple> tuples = new LinkedHashSet<>();
for (Tuple tuple : tupleSet) {
tuples.add(new NTuple(tuple.getElement(), tuple.getScore()));
}
return tuples;
}
catch (Exception e){
logger.error("Exception: ", e);
return null;
}
}
catch (Exception ex) {
logger.error("Exception: ", ex);
return null;
}
finally
{
if (redis != null)
{
returnResource(redis);
}
}
}
Here the method getResource returns a resource from JedisSentinelPool.
I want a pipelining method inside this class so that it takes a list of commands to execute and returns the responses as a list. I want that any construct of Jedis should not be used outside of RedisDBManager as in outside methods should call pipelining method which takes care of all responsibilities.
This question is similar to this question. It differs in a way that i want to use different redis commands as well and get their responses.
My current incomplete approach is modifying all methods in RedisDBManager to accept whether to pipeline it or not to a thread local Pipeline object and then have a pipelining method which syncs this pipeline object and returns the responses.
Something like :
public Set<NTuple> zrangeWithScores(String key, int min, int max, boolean pipelined) {
...
try
{
if (pipelined) {
pipeline = getExistingThreadLocalPipelineObject();
pipeline.zrangeWithScores(key, min, max);
} else {
redis = getResource();
...
return tuples;
}
catch (JedisConnectionException jex) {
...
if (pipelined) {
pipeline = getExistingThreadLocalPipelineObject();
pipeline.zrangeWithScores(key, min, max);
} else {
redis = getResource();
...
return tuples;
...
}
public List<MyResponse> syncPipeline() {
pipeline = getExistingThreadLocalPipelineObject();
pipeline.sync();
//process all responses and send
}
Is there any better or simpler approach? Thanks.
I have the following code which is executed asynchronously. I would like to make it synchronous in order to follow some logical flow but I cannot work out how.
You will see that scanning is set to true to indicate that the method is still working, at the beginning - I then initiate a findPrinters(...) command - this contains a DiscoveryHandler which runs asynchronously - foundPrinter() is called each time an item is discovered. discoveryFinished() is when the discovery process is successfully completed, and discoveryError(...) is called whenever an error occurs.
I rely on something being set in my DiscoveryHandler before I would like to return from this method. Hence why I have while (scanning) underneath it. But this feels like a hack to me, and not the correct way of doing things. I cannot get wait() and notify() working. Can someone tell me what the correct way to do this is please?
private boolean findPrinter(final Context ctx) {
try {
scanning = true;
BluetoothDiscoverer.findPrinters(ctx, new DiscoveryHandler() {
public void foundPrinter(DiscoveredPrinter device) {
if (device instanceof DiscoveredPrinterBluetooth) {
DiscoveredPrinterBluetooth btDevice = (DiscoveredPrinterBluetooth) device;
if (btDevice.friendlyName.startsWith("XXXX")) {
try {
connection = new BluetoothConnection(btDevice.address);
connection.open();
if (connection.isConnected()) {
address = btDevice.address;
}
} catch (Exception ex) {
}
}
}
}
public void discoveryFinished() {
scanning = false;
}
public void discoveryError(String arg0) {
scanning = false;
}
});
} catch (Exception ex) {
}
while (scanning) {}
return false;
}
You could do this with CountDownLatch, which might be the lightest synchronization primitive in java.util.concurrent:
private boolean findPrinter(final Context ctx) {
final CountDownLatch latch = new CountDownLatch(1);
final boolean[] result = {false};
...
BluetoothDiscoverer.findPrinters(ctx, new DiscoveryHandler() {
...
public void discoveryFinished() {
result[0] = true;
latch.countDown();
}
public void discoveryError(String arg0) {
result[0] = false;
latch.countDown();
}
...
}
// before final return
// wait for 10 seconds for the response
latch.await(10, TimeUnit.SECONDS);
//return the result, it will return false when there is timeout
return result[0];
}
There are a bunch of ways you can do this and wait()/notify() is probably not the best since you probably want to return something from your async method. As such I suggest using something like a BlockingQueue. Here is a simplified example of how you can do this:
private boolean findPrinter(final Context ctx) {
final BlockingQueue<?> asyncResult = new SynchronousQueue<?>();
try {
BluetoothDiscoverer.findPrinters(ctx, new DiscoveryHandler() {
public void foundPrinter(DiscoveredPrinter device) {
if (device instanceof DiscoveredPrinterBluetooth) {
DiscoveredPrinterBluetooth btDevice = (DiscoveredPrinterBluetooth) device;
if (btDevice.friendlyName.startsWith("XXXX")) {
try {
connection = new BluetoothConnection(btDevice.address);
connection.open();
if (connection.isConnected()) {
address = btDevice.address;
}
} catch (Exception ex) {
}
}
}
}
public void discoveryFinished() {
asyncResult.put(true);
}
public void discoveryError(String arg0) {
asyncResult.put(arg0);
}
});
} catch (Exception ex) {
}
Object result = asyncResult.take();
if (result instanceof Boolean) {
return (Boolean) result;
} else if (result instanceof String) {
logError((String) result);
}
return false;
}
One problem with using SynchronousQueue here though is that if discoveryFinished()/discoveryError() is called more than once, then the thread executing the code asynchronously will block forever since the SynchronousQueue assumes there will be exactly one take() per every put() and will block if a put() is made without a corresponding take() or vice versa. So if in your case those methods can be called more than once you would probably use a different kind of BlockingQueue instead (see documentation).