I have a logic where I need to save data in two tables(one-to-many). I have created two methods in my Java and I am trying to using Vertx Future with compose to implement the logic in sequence. But I have got half way and don't understand how to implement the compose when the first future is done. I mean code runs for the first future anAsyncAction_1(materialToAdd);, and the record is saved in the DB but now how do I call my second method in the compose
public Future<Void> anAsyncAction_2(final SendToCompanyFromSupplier rawmaterialToAdd, Integer id)
{
//code to get the id from the first future and save data in the table
}
Below is my code
public Future<Void> adddetails(final Supplier materialToAdd)
{
final Promise<Void> added = Promise.promise();
Future<Integer> fut1 = anAsyncAction_1(materialToAdd);
LOG.debug(" future.result() "+fut1.result());
fut1.compose((outcome) -> {
LOG.debug(" future.result() "+outcome);
});
CompositeFuture.all(fut1, fut2).onComplete(ar ->
{
System.out.println("BOTH OPERATION COMPLETED!! 1 " + ar.succeeded());
try
{
System.out.println("BOTH OPERATION COMPLETED!! 2 " + ar.result().list());
added.complete();
System.out.println("BOTH OPERATION COMPLETED!!");
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
});
return added.future();
}
If you just want to compose both the futures, the implementation can be simplified without using CompositeFuture or Promise.
Sample code:
public Future<Void> adddetails(final Object materialToAdd) {
Object rawMaterialToAdd = new Object();
return anAsyncAction_1(materialToAdd).compose(i -> anAsyncAction_2(rawMaterialToAdd, i))
.onComplete(ar -> {
if (ar.succeeded()) {
System.out.println("Both operations completed");
} else {
ar.cause()
.printStackTrace();
}
});
}
private Future<Integer> anAsyncAction_1(Object materialToAdd) {
Promise<Integer> promise = Promise.promise();
Vertx.currentContext()
.runOnContext(v -> promise.complete(1)); //Async Call. Replace with async DB call 1
return promise.future();
}
public Future<Void> anAsyncAction_2(final Object rawmaterialToAdd, Integer id) {
Promise<Void> promise = Promise.promise();
Vertx.currentContext()
.runOnContext(v -> {
System.out.println("Id received:" + id);
promise.complete();
}); //Async Call. Replace it with the async DB call 2
return promise.future();
}
Below is the sequence
Async DB call within anAsyncAction_1 will complete. The id returned will be used to complete the promise. The promise will inturn complete the first future.
The future will trigger anAsyncAction_2 with the id. Async DB call 2 on completion will complete the second future. Post the second future completion the onComplete handlers will be executed.
Related
Consider the code below. My foo line returns data. My bar line also returns data. However, if I were to comment out the foo line, then the bar line fails to return anything. It simply crashes the app without throwing an exception. Nor does it log anything in the Event Log.
Any thoughts as to why the Async method fails - only when not preceded by the non-Async?
private async Task<bool> MyTest() {
using(var cntx = new BoomiData.PantherDataContext(pantherConnectionString)) {
try {
//var foo = cntx.Manifests.FirstOrDefault();
var bar = await cntx.Manifests.FirstOrDefaultAsync();
}
catch(Exception ex) {
Console.WriteLine(ex.ToString());
}
}
return true;
}
I have a custom actor service, and I want to be able to add reminders to my actors from here, and not to have to call the actor directly for this. I do not want to wait to get in line if the actor is busy, but have it fire a reminder when needed.
I am trying to use StateProvider.SaveReminderAsync and from what the function summary says it should work exactly like I want.. but it does not, and I can not find a single sample online about this function. I've been looking for 2 days and tried a bunch of things to get it working with no luck.
Thanks for any help
public class ActorReminderTestServices : ActorService, IActorReminderTestService, IService
{
public ActorReminderTestServices(StatefulServiceContext context,
ActorTypeInformation actorTypeInfo,
Func<ActorService, ActorId, ActorBase> actorFactory = null,
Func<ActorBase, IActorStateProvider, IActorStateManager> stateManagerFactory = null,
IActorStateProvider stateProvider = null,
ActorServiceSettings settings = null)
: base(context, actorTypeInfo, actorFactory, stateManagerFactory, stateProvider, settings)
{
}
protected override async Task RunAsync(CancellationToken cancellationToken)
{
await base.RunAsync(cancellationToken);
#region Code to limit this to running just on 1 partition
FabricClient _fabricClient = new FabricClient();
System.Fabric.Query.ServicePartitionList _partitionList = await _fabricClient
.QueryManager.GetPartitionListAsync(this.Context.ServiceName);
System.Fabric.Query.Partition _1stPartition = _partitionList.OrderBy(a =>
(a.PartitionInformation as Int64RangePartitionInformation).LowKey).FirstOrDefault();
if (this.Partition.PartitionInfo.Id != _1stPartition.PartitionInformation.Id)
{
return;
}
#endregion
Task.Run(async () =>
{
await Task.Delay(10000);
await this.CreateActors(cancellationToken);
});
}
public async Task CreateActors(CancellationToken cancellationToken)
{
try
{
Guid _test2 = Guid.Parse("4C1DC22F-27DF-40C0-AD38-DC1971BDB281"); // {4C1DC22F-27DF-40C0-AD38-DC1971BDB281}
ActorId _actor2 = new ActorId(_test2);
// this function on the stateprovider.. how is this suppose to be used??
// from looking at the SF source this object that is used in the actual actor
// in an internal class, I tried creating my own version, but than the issues is it uses IActorManager
// and that is internal.. how can I use this function. I would like to loop through
// all the actors and add a reminder to all of them with out having to call the actor directly
await this.StateProvider.SaveReminderAsync(_actor2, new IActorReminder { Name = "testing", DueTime = TimeSpan.FromSeconds(1), Period = TimeSpan.FromSeconds(1), State = null }, cancellationToken);
}
catch (Exception ex)
{
}
}
}
}
Background:
During migration from JUnit4 to JUnit5 using VertX I read the migration guides which explain:
how to use the changed Promise and Future Vertx interfaces
how to VertxTestContext, Vertx auto-injection in Vertx Tests
how to use testContext.awaitCondition(), textContext.completing(), testContext.completeNow() etc.
Having this information in mind I wrote the following test:
Test Code:
import io.vertx.core.Promise;
import io.vertx.core.Future;
#ExtendWith(VertxExtension.class)
class RestApiTest {
#BeforeAll
static void setUpMongoDatabase() throws IOException {
(...)
}
#BeforeEach
void setUp(Vertx vertx, VertxTestContext ctx) {
vertx.deployVerticle(ApiVerticle.class.getName(), options, ctx.completing());
return WebClient.create(vertx);
}
#AfterEach
void tearDown(Vertx vertx, VertxTestContext testContext) {
assertThat(vertx.deploymentIDs().size(), is(equalTo(2)));
}
#AfterAll
static void stopMongoDatabase() {
(...)
}
#Test
void test(Vertx vertx, VertxTestContext testContext) {
Future<Void> insertFuture = insertTestData();
future.setHandler(testContext.completing());
// This ether throws a TimeoutException or does not block until the insert completed
testContext.awaitCompletion(5, TimeUnit.SECONDS);
// assert
mongoClient.findOne(COLLECTION, QUERY, result -> {
if (result.succeeded()) testContext.completeNow();
else testContext.failNow();
});
}
Future<Void> insertTestData() {
Promise<Void> promise = Promise.promise();
Future<Void> future = promise.future();
mongoClient.insert(COLLECTION, QUERY, result -> {
if (result.succeeded()) {
promise.complete();
} else {
promise.fail();
}
});
return future;
}
}
Problem:
testContext.awaitCompletion() ether throws a TimeoutException
or does not block until the async insert completed so that my assert returns successfully
Question:
How can I wait for the async mongo query to complete before I continue with my test?
The problem was that I am using the VertX Promise and Future classes:
those classes only work on a VertX Verticle
my insertTestData() method is not executed on a Verticle
One solution is to use the java.util.concurrent.ReentrantLock and Condition classes instead:
#Test
void test() {
insertTestData(); // This is now synchronous as required
// assert
mongoClient.findOne(COLLECTION, QUERY, result -> {
if (result.succeeded()) testContext.completeNow();
else testContext.failNow();
});
}
void insertTestData() {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
mongoClient.insert(COLLECTION, QUERY, result -> {
if (result.succeeded()) {
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
} else {
fail();
}
});
lock.lock();
try {
condition.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} finally {
lock.unlock();
}
}
}
I have the following code, which stores information in two different tables in the same method
public static async Task<Response> AddStockTransaction(StockTransactionsHeader header, List<StockTransactionsDetails> details)
{
using (DataContext dbContext = new DataContext())
{
try
{
dbContext.StockTransactionsHeader.Add(header);
await dbContext.SaveChangesAsync();
int hearderID = header.TransactionHeaderID;
foreach (var item in details)
{
item.TransactionHeaderID = hearderID;
}
dbContext.StockTransactionsDetails.AddRange(details);
await dbContext.SaveChangesAsync();
return new Response
{
IsSuccess = true
};
}
catch (Exception ex)
{
return new Response
{
IsSuccess = false,
Message = ex.Message
};
}
}
}
How can I do, in case there is an exception in the second SaveChanges () to revert the first one?
Once SaveChanges has been called, your datat is stored on your database. You should not call SaveChanges more than once in a call, unless you are willingly to persist the intermediate steps.
You can use a transaction scope to create managed transactions :
using (TransactionScope scope = CreateTransactionScope())
{
DoSomthing(context);
scope.Complete();
}
however, if the failure of the second part involves rolling back the first one, this means that both parts belong to the same transaction, therefore simply omitting the first SaveChanges would turn your code into a single transaction.
From my another awnser: You could use DbTransaction class.
private void TestTransaction()
{
var context = new MyContext(connectionString);
using (var transaction = context.Database.BeginTransaction())
{
try
{
// do your stuff
// commit changes
transaction.Commit();
}
catch
{
// 'undo' all changes
transaction.Rollback();
}
}
}
I have a client server application and I'm using rxjava to do server requests from the client. The client should only do one request at a time so I intent to use a thread queue scheduler similar to the trampoline scheduler.
Now I try to implement a mechanism to watch changes on the server. Therefore I send a long living request that blocks until the server has some changes and sends back the result (long pull).
This long pull request should only run when the job queue is idle. I'm looking for a way to automatically stop the watch request when a regular request is scheduled and start it again when the queue becomes empty. I thought about modifying the trampoline scheduler to get this behavior but I have the feeling that this is a common problem and there might be an easier solution?
You can hold onto the Subscription returned by scheduling the long poll task, unsubscribe it if the queue becomes non-empty and re-schedule if the queue becomes empty.
Edit: here is an example with the basic ExecutorScheduler:
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class IdleScheduling {
static final class TaskQueue {
final ExecutorService executor;
final AtomicReference<Future<?>> idleFuture;
final Runnable idleRunnable;
final AtomicInteger wip;
public TaskQueue(Runnable idleRunnable) {
this.executor = Executors.newFixedThreadPool(1);
this.idleRunnable = idleRunnable;
this.idleFuture = new AtomicReference<>();
this.wip = new AtomicInteger();
this.idleFuture.set(executor.submit(idleRunnable));
}
public void shutdownNow() {
executor.shutdownNow();
}
public Future<?> enqueue(Runnable task) {
if (wip.getAndIncrement() == 0) {
idleFuture.get().cancel(true);
}
return executor.submit(() -> {
task.run();
if (wip.decrementAndGet() == 0) {
startIdle();
}
});
}
void startIdle() {
idleFuture.set(executor.submit(idleRunnable));
}
}
public static void main(String[] args) throws Exception {
TaskQueue tq = new TaskQueue(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
System.out.println("Idle interrupted...");
return;
}
System.out.println("Idle...");
}
});
try {
Thread.sleep(1500);
tq.enqueue(() -> System.out.println("Work 1"));
Thread.sleep(500);
tq.enqueue(() -> {
System.out.println("Work 2");
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
});
tq.enqueue(() -> System.out.println("Work 3"));
Thread.sleep(1500);
} finally {
tq.shutdownNow();
}
}
}