The following Trigger is firing twice:
trigger AccountTrigger on Account ( before insert, after insert, before update, after update, before delete, after delete) {
AccountTriggerHandler handle = new AccountTriggerHandler(trigger.new, trigger.oldMap);
System.debug('AccountTrigger created a handler instance: ' + handle);
// Currently the Trigger is firing twice with no obvious reason.
if (Trigger.isBefore) {
if (Trigger.isInsert) {
handle.beforeInsert();
}
if (Trigger.isUpdate) {
// Call handler here!
}
if (Trigger.isDelete) {
// Call handler here!
}
}
if (Trigger.isAfter) {
if (Trigger.isInsert) {
// Call handler here!
}
if (Trigger.isUpdate) {
// Call handler here!
}
if (Trigger.isDelete) {
// Call handler here!
}
}
}
The debug result is showing two handler instances. The weird thing is: The first one seems to be empty? How can that be?
EDIT 1:
The Testcode:
#isTest
public class AccountTestTest {
#isTest
public static void testAccountInsert() {
// Insert an Account
Account a = new Account(name='TestCustomer');
insert a;
Account queryAccount = [SELECT Account.id, Account.name FROM Account WHERE Id = :a.Id];
System.debug('TEST RESULT: ' + queryAccount);
System.debug('AccountTestTest completed.');
// Actually test something...
}
}
I know it's missing asserts, but for the sake of simplicity, I just tried this one.
It's because of ”before insert”. At that stage ids haven't been generated yet. If you don't have any logic that fits into before insert best (complex validations? Field prepopulation?) remove that event?
Related
I have an account service and a product service communicating. When a request comes from a user to purchase a product (I did not include the user service, it is working fine and not the issue), the product service checks to see if there are enough funds in the account, and if there is it updates the balances. The following code works fine:
#GetMapping("/account/{userId}/product/{productId}")
public Mono<ResponseEntity<Product>> checkAccount(#PathVariable("userId") int userId,#PathVariable("productId") int productId){
Mono<Account> account = webClientBuilder.build().get().uri("http://account-service/user/accounts/{userId}/",userId)
.retrieve().bodyToMono(Account.class);
Mono<Product> product = this.ps.findById(productId);
Mono<Boolean> result = account.zipWith(product,this::isAccountBalanceGreater);
Mono<ResponseEntity<Product>> p = result.zipWith(product,this::getResponse);
return p;
}
public boolean isAccountBalanceGreater(Account acc, Product prd) {
return(acc.getBalance()>=prd.getPrice()):
}
public ResponseEntity<Product> getResponse(boolean result,Product prod){
if(result) {
return ResponseEntity.accepted().body(prod);
}else {
return ResponseEntity.badRequest().body(prod);
}
}
My put method in the account service also works fine:
#PutMapping("/account/update/{accountId}")
public Mono<ResponseEntity<Account>> updateAccount(#PathVariable("accountId") int accountId, #RequestBody Account account) {
return as.findById(accountId)
.flatMap(oldAcc->{
oldAcc.setAccountId(account.getAccountId());
oldAcc.setAccountId(account.getAccountId());
oldAcc.setOwner(account.getOwner());
oldAcc.setPin(account.getPin());
oldAcc.setBalance(account.getBalance());
oldAcc.setUserId(account.getUserId());
return ar.save(oldAcc);
}).map(a -> ResponseEntity.ok(a))
.defaultIfEmpty(ResponseEntity.notFound().build());
}
Now I want to be able to update the balances, I have tried this in the isAccountBalancerGreater method:
public boolean isAccountBalanceGreater(Account acc, Product prd) {
if(acc.getBalance() >= prd.getPrice()) {
double newBuyerBalance =acc.getBalance() - prd.getPrice();
Account newOwnerAcc = new Account(acc.getAccountId(),acc.getOwner(),acc.getPin(),newBuyerBalance,acc.getUserId());
this.ps.removeProduct(prd.getProductId());
webClientBuilder.build().put().uri("http://account-service/account/update/{accountId}",acc.getAccountId()).body(newOwnerAcc,Account.class).exchange();
return true;
}
return false;
}
However this does not work, not error just nothing updates.
My test case works when I run the same code with a test account. I'm not sure why this is not executing. Any suggestions?
you have to think of reactive code as event chains or callbacks. So you need to respond to what you want something to do, after some other thing has been completed.
return webClientBuilder.build()
.put().uri("http://account-service/account/update/{accountId}",
acc.getAccountId())
.body(newOwnerAcc,Account.class)
.exchange()
.thenReturn(true); // if you really need to return a boolean
return a boolean is usually not semantically correct in a reactive world. Its very common to try to avoid if-else statements
One way is to return a Mono<Void> to mark that something has been completed, and trigger something chained onto it.
public Mono<Void> isAccountBalanceGreater(Account acc, Product prd) {
return webclient.put()
.uri( ... )
.retrieve()
.bodyToMono(Void.class)
.doOnError( // handle error )
}
// How to call for example
isAccountBalanceGreater(foo, bar)
.doOnSuccess( ... )
.doOnError( ... )
Is there a way to wait for a future to complete without blocking the event loop?
An example of a use case with querying Mongo:
Future<Result> dbFut = Future.future();
mongo.findOne("myusers", myQuery, new JsonObject(), res -> {
if(res.succeeded()) {
...
dbFut.complete(res.result());
}
else {
...
dbFut.fail(res.cause());
}
}
});
// Here I need the result of the DB query
if(dbFut.succeeded()) {
doSomethingWith(dbFut.result());
}
else {
error();
}
I know the doSomethingWith(dbFut.result()); can be moved to the handler, yet if it's long, the code will get unreadable (Callback hell ?) It that the right solution ? Is that the omny solution without additional libraries ?
I'm aware that rxJava simplifies the code, but as I don't know it, learning Vert.x and rxJava is just too much.
I also wanted to give a try to vertx-sync. I put the dependency in the pom.xml; everything got downloaded fine but when I started my app, I got the following error
maurice#mickey> java \
-javaagent:~/.m2/repository/co/paralleluniverse/quasar-core/0.7.5/quasar-core-0.7.5-jdk8.jar \
-jar target/app-dev-0.1-fat.jar \
-conf conf/config.json
Error opening zip file or JAR manifest missing : ~/.m2/repository/co/paralleluniverse/quasar-core/0.7.5/quasar-core-0.7.5-jdk8.jar
Error occurred during initialization of VM
agent library failed to init: instrument
I know what the error means in general, but I don't know in that context... I tried to google for it but didn't find any clear explanation about which manifest to put where. And as previously, unless mandatory, I prefer to learn one thing at a time.
So, back to the question : is there a way with "basic" Vert.x to wait for a future without perturbation on the event loop ?
You can set a handler for the future to be executed upon completion or failure:
Future<Result> dbFut = Future.future();
mongo.findOne("myusers", myQuery, new JsonObject(), res -> {
if(res.succeeded()) {
...
dbFut.complete(res.result());
}
else {
...
dbFut.fail(res.cause());
}
}
});
dbFut.setHandler(asyncResult -> {
if(asyncResult.succeeded()) {
// your logic here
}
});
This is a pure Vert.x way that doesn't block the event loop
I agree that you should not block in the Vertx processing pipeline, but I make one exception to that rule: Start-up. By design, I want to block while my HTTP server is initialising.
This code might help you:
/**
* #return null when waiting on {#code Future<Void>}
*/
#Nullable
public static <T>
T awaitComplete(Future<T> f)
throws Throwable
{
final Object lock = new Object();
final AtomicReference<AsyncResult<T>> resultRef = new AtomicReference<>(null);
synchronized (lock)
{
// We *must* be locked before registering a callback.
// If result is ready, the callback is called immediately!
f.onComplete(
(AsyncResult<T> result) ->
{
resultRef.set(result);
synchronized (lock) {
lock.notify();
}
});
do {
// Nested sync on lock is fine. If we get a spurious wake-up before resultRef is set, we need to
// reacquire the lock, then wait again.
// Ref: https://stackoverflow.com/a/249907/257299
synchronized (lock)
{
// #Blocking
lock.wait();
}
}
while (null == resultRef.get());
}
final AsyncResult<T> result = resultRef.get();
#Nullable
final Throwable t = result.cause();
if (null != t) {
throw t;
}
#Nullable
final T x = result.result();
return x;
}
I want to repeat a Single based on the single value emitted in onSuccess(). Here is a working example
import org.reactivestreams.Publisher;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.functions.Function;
public class Temp {
void main() {
Job job = new Job();
Single.just(job)
.map(this::processJob)
.repeatWhen(new Function<Flowable<Object>, Publisher<?>>() {
#Override
public Publisher<?> apply(Flowable<Object> objectFlowable) throws Exception {
// TODO repeat when Single emits false
return null;
}
})
.subscribe();
}
/**
* returns true if process succeeded, false if failed
*/
boolean processJob(Job job) {
return true;
}
class Job {
}
}
I understand how repeatWhen works for Observables by relying on the "complete" notification. However since Single doesn't receive that notification I'm not sure what the Flowable<Object> is really giving me. Also why do I need to return a Publisher from this function?
Instead of relying on a boolean value, you could make your job throw an exception when it fails:
class Job {
var isSuccess: Boolean = false
}
fun processJob(job: Job): String {
if (job.isSuccess) {
return "job succeeds"
} else {
throw Exception("job failed")
}
}
val job = Job()
Single.just(job)
.map { processJob(it) }
.retry() // will resubscribe until your job succeeds
.subscribe(
{ value -> print(value) },
{ error -> print(error) }
)
i saw a small discrepancy in the latest docs and your code, so i did a little digging...
(side note - i think the semantics of retryWhen seem like the more appropriate operator for your case, so i've substituted it in for your usage of repeatWhen. but i think the root of your problem remains the same in either case).
the signature for retryWhen is:
retryWhen(Function<? super Flowable<Throwable>,? extends Publisher<?>> handler)
that parameter is a factory function whose input is a source that emits anytime onError is called upstream, giving you the ability to insert custom retry logic that may be influenced through interrogation of the underlying Throwable. this begins to answer your first question of "I'm not sure what the Flowable<Object> is really giving me" - it shouldn't be Flowable<Object> to begin with, it should be Flowable<Throwable> (for the reason i just described).
so where did Flowable<Object> come from? i managed to reproduce IntelliJ's generation of this code through it's auto-complete feature using RxJava version 2.1.17. upgrading to 2.2.0, however, produces the correct result of Flowable<Throwable>. so, see if upgrading to the latest version generates the correct result for you as well.
as for your second question of "Also why do I need to return a Publisher from this function?" - this is used to determine if re-subscription should happen. if the factory function returns a Publisher that emits a terminal state (ie calls onError() or onComplete()) re-subscription will not happen. however, if onNext() is called, it will. (this also explains why the Publisher isn't typed - the type doesn't matter. the only thing that does matter is what kind of notification it publishes).
another way to rewrite this, incorporating the above, might be as follows:
// just some type to use as a signal to retry
private class SpecialException extends RuntimeException {}
// job processing results in a Completable that either completes or
// doesn't (by way of an exception)
private Completable rxProcessJob(Job job) {
return Completable.complete();
// return Completable.error(new SpecialException());
}
...
rxProcessJob(new Job())
.retryWhen(errors -> {
return errors.flatMap(throwable -> {
if(throwable instanceof SpecialException) {
return PublishProcessor.just(1);
}
return PublishProcessor.error(throwable);
});
})
.subscribe(
() -> {
System.out.println("## onComplete()");
},
error -> {
System.out.println("## onError(" + error.getMessage() + ")");
}
);
i hope that helps!
The accepted answer would work, but is hackish. You don't need to throw an error; simply filter the output of processJob which converts the Single to a Maybe, and then use the repeatWhen handler to decide how many times, or with what delay, you may want to resubscribe. See Kotlin code below from a working example, you should be able to easily translate this to Java.
filter { it }
.repeatWhen { handler ->
handler.zipWith(1..3) { _, i -> i }
.flatMap { retryCount -> Flowable.timer(retryDelay.toDouble().pow(retryCount).toLong(), TimeUnit.SECONDS) }
.doOnNext { log.warn("Retrying...") }
}
I would like to merge two Single<MyData> such that if one of them fails but the other one succeeds then the error of the one that failed and the emission from the other one are reported, and then the resulting Single<MyData> (or Observable<MyData>) completes.
If both Single<MyData> fail then the result should also fail and also be marked as failed.
What I would like to have at the end is:
If both succeed then the emitted values and a producer marked as completed.
If one succeeds and the other fails, the emitted value, the thrown error and the producer marked as complete.
If all fail, the errors and the producer marked as failed.
It's like an 'OR' operation
This is not possible. There is only a single terminal event allowed. The contract for Single is success|error. If you need to receive a next event as well, you should consider to use Observable instead. The contract for Observable is next* complete|error, but you'll still not get a complete.
Observable.mergeDelayError(single1.toObservable(), single2.toObservable())
This can be accomplished with Single.create(SingleOnSubscribe). If your return type is Single<MyData> only one of the responses can be returned, but you could also modify this to instead return a Single<List<MyData>> or some other RxJava structure like Flowable<MyData> that supports multiple returns. In this example, the Single<MyData> returns whichever call returns last because that was the simplest to implement.
public Single<MyData> getCombinedSingle(List<Single<MyData>> singles) {
return Single.create(new SingleOnSubscribe<MyData> {
private boolean encounteredError = false;
private MyData myData;
#Override
public void subscribe(#NonNull Emitter<MyData> emitter) {
List<Disposable> disposables = new ArrayList<>();
Consumer<MyData> myDataConsumer = myData -> {
this.MyData = myData;
checkForFinish(emitter, disposables);
}
Consumer<Throwable> throwableConsumer = throwable -> {
throwable.printStackTrace();
encounteredError = true;
checkForFinish(emitter, disposables);
}
for (Single single: singles) {
disposables.put(single.subscribe(myDataConsumer, throwableConsumer);
}
}
private void checkForFinish(SingleEmitter<MyData> emitter, List<Disposable> disposables) {
if (disposables1.stream().allMatch(Disposable::isDisposed)) {
if (encounteredError) {
emitter.onError(new Throwable());
} else {
emitter.onSuccess(myData);
}
}
}
}
}
This could be modified to return a Throwable from the original Singles if needed.
UPDATED: I have a piece of code that creates records when they don't exist, or updates them when they do exist. However, while trying to update the records I get this exception:
Microsoft.EntityFrameworkCore.DbUpdateException The DELETE statement
conflicted with the REFERENCE constraint
public static string AddCurrencies(ApplicationDbContext db)
{
// ...
foreach (Currency c in db.Currency.ToList())
{
try
{
db.Remove(c); // the troublemaker!
db.SaveChanges();
}
catch
{
// probably in use (foreign key)
}
}
// ...
foreach (Currency c in CurrencyList)
{
var c_db = db.Currency.FirstOrDefault(x => x.Code == c.Code);
if (c_db == null)
{
// adding
db.Currency.Add(c);
}
else
{
// updating
c_db.Name = c.Name;
c_db.LocalDisplay = c.LocalDisplay;
}
db.SaveChanges(); // exception fired if updating!
}
// ...
}
After some investigation, and having being able to turn SQL debugging on, I found out that the Remove() "persists" and that it will be retried with the second call to SaveChanges(), hence the exception. Now the question is reformulated: how do I "undo" (in the lack of a better expression) the Remove() commands that failed?
I managed to solve this issue this way:
var entry = context.Entry(entity);
entry.Reload();
for each entry where delete failed.