forked from restatedev/e2e
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds new e2e tests which ensure that cancellation works. The following call chains are canceled: * CancelTestService/StartTest --> BlockingService/Block --> BlockingSerivce/Block (inboxed call since BlockingService is singleton) * CancelTestService/StartTest --> BlockingService/Block --> uncompleted awakeable * CancelTestService/StartTest --> BlockingService/Block --> very long sleep This fixes restatedev#228.
- Loading branch information
1 parent
f91ed8b
commit abdabfe
Showing
8 changed files
with
391 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright (c) 2023 - Restate Software, Inc., Restate GmbH | ||
// | ||
// This file is part of the Restate e2e tests, | ||
// which are released under the MIT license. | ||
// | ||
// You can find a copy of the license in file LICENSE in the root | ||
// directory of this repository or package, or at | ||
// https://github.com/restatedev/e2e/blob/main/LICENSE | ||
|
||
syntax = "proto3"; | ||
|
||
option java_package = "dev.restate.e2e.services.canceltest"; | ||
option java_outer_classname = "CancelTestProto"; | ||
|
||
import "google/protobuf/empty.proto"; | ||
import "dev/restate/ext.proto"; | ||
|
||
package cancel_test; | ||
|
||
service CancelTestService { | ||
option (dev.restate.ext.service_type) = SINGLETON; | ||
|
||
rpc StartTest(BlockingRequest) returns (google.protobuf.Empty); | ||
rpc VerifyTest(google.protobuf.Empty) returns (Response); | ||
} | ||
|
||
enum BlockingOperation { | ||
CALL = 0; | ||
SLEEP = 1; | ||
AWAKEABLE = 2; | ||
} | ||
|
||
message BlockingRequest { | ||
BlockingOperation operation = 1; | ||
} | ||
|
||
message Response { | ||
bool is_canceled = 1; | ||
} | ||
|
||
service BlockingService { | ||
option (dev.restate.ext.service_type) = SINGLETON; | ||
|
||
rpc Block(BlockingRequest) returns (google.protobuf.Empty); | ||
rpc IsUnlocked(google.protobuf.Empty) returns (google.protobuf.Empty); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
...ices/java-services/src/main/java/dev/restate/e2e/services/canceltest/BlockingService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright (c) 2023 - Restate Software, Inc., Restate GmbH | ||
// | ||
// This file is part of the Restate e2e tests, | ||
// which are released under the MIT license. | ||
// | ||
// You can find a copy of the license in file LICENSE in the root | ||
// directory of this repository or package, or at | ||
// https://github.com/restatedev/e2e/blob/main/LICENSE | ||
|
||
package dev.restate.e2e.services.canceltest; | ||
|
||
import dev.restate.e2e.services.awakeableholder.AwakeableHolderProto; | ||
import dev.restate.e2e.services.awakeableholder.AwakeableHolderServiceRestate; | ||
import dev.restate.sdk.Awakeable; | ||
import dev.restate.sdk.RestateContext; | ||
import dev.restate.sdk.common.CoreSerdes; | ||
import dev.restate.sdk.common.TerminalException; | ||
import java.time.Duration; | ||
|
||
public class BlockingService extends BlockingServiceRestate.BlockingServiceRestateImplBase { | ||
@Override | ||
public void block(RestateContext context, CancelTestProto.BlockingRequest request) | ||
throws TerminalException { | ||
final BlockingServiceRestate.BlockingServiceRestateClient self = | ||
BlockingServiceRestate.newClient(context); | ||
final AwakeableHolderServiceRestate.AwakeableHolderServiceRestateClient client = | ||
AwakeableHolderServiceRestate.newClient(context); | ||
|
||
Awakeable<String> awakeable = context.awakeable(CoreSerdes.STRING_UTF8); | ||
client | ||
.hold( | ||
AwakeableHolderProto.HoldRequest.newBuilder() | ||
.setName("cancel") | ||
.setId(awakeable.id()) | ||
.build()) | ||
.await(); | ||
awakeable.await(); | ||
|
||
switch (request.getOperation()) { | ||
case CALL: | ||
self.block(request).await(); | ||
break; | ||
case SLEEP: | ||
context.sleep(Duration.ofDays(1024)); | ||
break; | ||
case AWAKEABLE: | ||
Awakeable<String> uncompletable = context.awakeable(CoreSerdes.STRING_UTF8); | ||
uncompletable.await(); | ||
break; | ||
default: | ||
throw new IllegalArgumentException("Unknown operation: " + request.getOperation()); | ||
} | ||
} | ||
|
||
@Override | ||
public void isUnlocked(RestateContext context) throws TerminalException { | ||
// no-op | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
...es/java-services/src/main/java/dev/restate/e2e/services/canceltest/CancelTestService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Copyright (c) 2023 - Restate Software, Inc., Restate GmbH | ||
// | ||
// This file is part of the Restate e2e tests, | ||
// which are released under the MIT license. | ||
// | ||
// You can find a copy of the license in file LICENSE in the root | ||
// directory of this repository or package, or at | ||
// https://github.com/restatedev/e2e/blob/main/LICENSE | ||
|
||
package dev.restate.e2e.services.canceltest; | ||
|
||
import dev.restate.sdk.RestateContext; | ||
import dev.restate.sdk.common.CoreSerdes; | ||
import dev.restate.sdk.common.StateKey; | ||
import dev.restate.sdk.common.TerminalException; | ||
|
||
public class CancelTestService extends CancelTestServiceRestate.CancelTestServiceRestateImplBase { | ||
private static StateKey<Boolean> CANCELED_STATE = StateKey.of("canceled", CoreSerdes.BOOLEAN); | ||
|
||
@Override | ||
public void startTest(RestateContext ctx, CancelTestProto.BlockingRequest request) | ||
throws TerminalException { | ||
BlockingServiceRestate.BlockingServiceRestateClient client = | ||
BlockingServiceRestate.newClient(ctx); | ||
|
||
try { | ||
client.block(request).await(); | ||
} catch (TerminalException e) { | ||
if (e.getCode() == TerminalException.Code.CANCELLED) { | ||
ctx.set(CANCELED_STATE, true); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public CancelTestProto.Response verifyTest(RestateContext context) throws TerminalException { | ||
return CancelTestProto.Response.newBuilder() | ||
.setIsCanceled(context.get(CANCELED_STATE).orElse(false)) | ||
.build(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// Copyright (c) 2023 - Restate Software, Inc., Restate GmbH | ||
// | ||
// This file is part of the Restate e2e tests, | ||
// which are released under the MIT license. | ||
// | ||
// You can find a copy of the license in file LICENSE in the root | ||
// directory of this repository or package, or at | ||
// https://github.com/restatedev/e2e/blob/main/LICENSE | ||
|
||
import * as restate from "@restatedev/restate-sdk"; | ||
|
||
import { | ||
CancelTestService as ICancelTestService, | ||
BlockingService as IBlockingService, | ||
protobufPackage, | ||
BlockingServiceClientImpl, | ||
Response, | ||
BlockingOperation, | ||
BlockingRequest, | ||
} from "./generated/cancel_test"; | ||
import { Empty } from "./generated/google/protobuf/empty"; | ||
import { AwakeableHolderServiceClientImpl } from "./generated/awakeable_holder"; | ||
|
||
export const CancelTestServiceFQN = protobufPackage + ".CancelTestService"; | ||
export const BlockingServiceFQN = protobufPackage + ".BlockingService"; | ||
|
||
export class CancelTestService implements ICancelTestService { | ||
async verifyTest(request: Empty): Promise<Response> { | ||
const ctx = restate.useContext(this); | ||
const isCanceled = await ctx.get<boolean>("canceled") ?? false; | ||
|
||
return Response.create({ isCanceled: isCanceled }); | ||
} | ||
|
||
async startTest(request: BlockingRequest): Promise<Empty> { | ||
const ctx = restate.useContext(this); | ||
const client = new BlockingServiceClientImpl(ctx); | ||
|
||
try { | ||
await client.block(request); | ||
} catch (e) { | ||
if (e instanceof restate.TerminalError && (e as restate.TerminalError).code === restate.ErrorCodes.CANCELLED) { | ||
ctx.set("canceled", true); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
|
||
return Empty.create({}); | ||
} | ||
|
||
} | ||
|
||
export class BlockingService implements IBlockingService { | ||
async block(request: BlockingRequest): Promise<Empty> { | ||
const ctx = restate.useContext(this); | ||
const client = new AwakeableHolderServiceClientImpl(ctx); | ||
const self = new BlockingServiceClientImpl(ctx); | ||
|
||
const { id, promise } = ctx.awakeable(); | ||
client.hold({ name: "cancel", id }) | ||
await promise; | ||
|
||
switch (request.operation) { | ||
case BlockingOperation.CALL: { | ||
await self.block(request); | ||
break; | ||
} | ||
case BlockingOperation.SLEEP: { | ||
await ctx.sleep(1_000_000_000); | ||
break; | ||
} | ||
case BlockingOperation.AWAKEABLE: { | ||
const { id: _, promise: uncompletable_promise } = ctx.awakeable(); | ||
await uncompletable_promise; | ||
break; | ||
} | ||
} | ||
|
||
return Empty.create({}); | ||
} | ||
|
||
async isUnlocked(request: Empty): Promise<Empty> { | ||
return Empty.create({}); | ||
} | ||
} |
Oops, something went wrong.