Skip to content

Commit

Permalink
Fix invoke actor after aborted flow.
Browse files Browse the repository at this point in the history
Signed-off-by: Artur Souza <asouza.pro@gmail.com>
  • Loading branch information
artursouza committed Aug 5, 2023
1 parent 0488e9f commit 7cf8b41
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 24 deletions.
33 changes: 17 additions & 16 deletions sdk-actors/src/main/java/io/dapr/actors/runtime/AbstractActor.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.IOException;
import java.time.Duration;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* Represents the base class for actors.
Expand All @@ -28,8 +29,6 @@
*/
public abstract class AbstractActor {

private static final ActorObjectSerializer INTERNAL_SERIALIZER = new ActorObjectSerializer();

/**
* Type of tracing messages.
*/
Expand Down Expand Up @@ -58,7 +57,7 @@ public abstract class AbstractActor {
/**
* Internal control to assert method invocation on start and finish in this SDK.
*/
private boolean started;
private final AtomicBoolean started;

/**
* Instantiates a new Actor.
Expand All @@ -74,7 +73,7 @@ protected AbstractActor(ActorRuntimeContext runtimeContext, ActorId id) {
runtimeContext.getActorTypeInformation().getName(),
id);
this.actorTrace = runtimeContext.getActorTrace();
this.started = false;
this.started = new AtomicBoolean(false);
}

/**
Expand Down Expand Up @@ -250,14 +249,16 @@ protected Mono<Void> saveState() {

/**
* Resets the cached state of this Actor.
*
* @param force Forces the rollback, even if not in a call.
*/
void rollback() {
if (!this.started) {
void rollback(boolean force) {
if (!force && !this.started.get()) {
throw new IllegalStateException("Cannot reset state before starting call.");
}

this.resetState();
this.started = false;
this.started.set(false);
}

/**
Expand Down Expand Up @@ -302,11 +303,12 @@ Mono<Void> onDeactivateInternal() {
*/
Mono<Void> onPreActorMethodInternal(ActorMethodContext actorMethodContext) {
return Mono.fromRunnable(() -> {
if (this.started) {
throw new IllegalStateException("Cannot invoke a method before completing previous call.");
if (this.started.get()) {
throw new IllegalStateException(
"Cannot invoke a method before completing previous call. " + getId().toString());
}

this.started = true;
this.started.set(true);
}).then(this.onPreActorMethod(actorMethodContext));
}

Expand All @@ -318,14 +320,13 @@ Mono<Void> onPreActorMethodInternal(ActorMethodContext actorMethodContext) {
*/
Mono<Void> onPostActorMethodInternal(ActorMethodContext actorMethodContext) {
return Mono.fromRunnable(() -> {
if (!this.started) {
if (!this.started.get()) {
throw new IllegalStateException("Cannot complete a method before starting a call.");
}
}).then(this.onPostActorMethod(actorMethodContext))
.then(this.saveState())
.then(Mono.fromRunnable(() -> {
this.started = false;
}));
})
.then(this.onPostActorMethod(actorMethodContext))
.then(this.saveState())
.then(Mono.fromRunnable(() -> this.started.set(false)));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,15 +306,16 @@ private <T> Mono<T> invoke(ActorId actorId, ActorMethodContext context, Function
this.runtimeContext.getActorTypeInformation().getName()));
}

return actor.onPreActorMethodInternal(context)
return Mono.fromRunnable(() -> actor.rollback(true))
.onErrorMap(throwable -> {
actor.rollback(false);
return throwable;
})
.then(actor.onPreActorMethodInternal(context))
.then((Mono<Object>) func.apply(actor))
.switchIfEmpty(
actor.onPostActorMethodInternal(context))
.flatMap(r -> actor.onPostActorMethodInternal(context).thenReturn(r))
.onErrorMap(throwable -> {
actor.rollback();
return throwable;
})
.map(o -> (T) o);
} catch (Exception e) {
return Mono.error(e);
Expand Down
5 changes: 4 additions & 1 deletion sdk-tests/components/mongo-statestore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ spec:
- name: databaseName
value: local
- name: collectionName
value: testCollection
value: testCollection
scopes:
- grpcstateclientit
- httpstateclientit
2 changes: 1 addition & 1 deletion sdk-tests/src/test/java/io/dapr/it/DaprRun.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class DaprRun implements Stoppable {

private static final String DAPR_RUN = "dapr run --app-id %s --app-protocol %s " +
"--config ./configurations/configuration.yaml " +
"--components-path ./components";
"--resources-path ./components";

// the arg in -Dexec.args is the app's port
private static final String DAPR_COMMAND =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import io.dapr.actors.ActorId;
import io.dapr.actors.client.ActorProxyBuilder;
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import io.dapr.client.resiliency.ResiliencyOptions;
import io.dapr.it.BaseIT;
import io.dapr.it.DaprRun;
Expand All @@ -41,7 +43,7 @@ public class ActorSdkResiliencytIT extends BaseIT {

private static final int NUM_ITERATIONS = 20;

private static final Duration TIMEOUT = Duration.ofMillis(100);
private static final Duration TIMEOUT = Duration.ofMillis(1000);

private static final Duration LATENCY = TIMEOUT.dividedBy(2);

Expand All @@ -51,6 +53,8 @@ public class ActorSdkResiliencytIT extends BaseIT {

private static DaprRun daprRun;

private static DaprClient daprClient;

private static DemoActor demoActor;

private static ToxiProxyRun toxiProxyRun;
Expand All @@ -75,6 +79,7 @@ public static void init() throws Exception {
// HTTP client is deprecated, so SDK resiliency is for gRPC client only.
daprRun.switchToGRPC();
demoActor = buildDemoActorProxy(null);
daprClient = new DaprClientBuilder().build();

toxiProxyRun = new ToxiProxyRun(daprRun, LATENCY, JITTER);
toxiProxyRun.start();
Expand Down

0 comments on commit 7cf8b41

Please sign in to comment.