Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 148 additions & 3 deletions src/workerd/server/server-test.c++
Original file line number Diff line number Diff line change
Expand Up @@ -1998,9 +1998,9 @@ KJ_TEST("Server: Durable Objects (in memory)") {
` throw new Error("durable ID should be type DurableObjectId, " +
` `got: ${this.id.constructor.name}`);
` }
` if (this.id.name) {
` throw new Error("ctx.id for Durable Object should not have a .name " +
` `property, got: ${this.id.name}`);
` if (typeof this.id.name !== "string" || this.id.name.length === 0) {
Comment thread
maxmcd marked this conversation as resolved.
` throw new Error("ctx.id.name should be a non-empty string for " +
` `named DOs, got: ${JSON.stringify(this.id.name)}`);
` }
Comment thread
maxmcd marked this conversation as resolved.
` }
` async fetch(request) {
Expand Down Expand Up @@ -2047,6 +2047,151 @@ KJ_TEST("Server: Durable Objects (in memory)") {
"/bar", "02b496f65dd35cbac90e3e72dc5a398ee93926ea4a3821e26677082d2e6f9b79: http://foo/bar 2");
}

KJ_TEST("Server: Durable Objects keep ctx.id.name undefined for unique IDs") {
TestServer test(R"((
services = [
( name = "hello",
worker = (
compatibilityDate = "2022-08-17",
modules = [
( name = "main.js",
esModule =
`export default {
` async fetch(request, env) {
` let actor = env.ns.get(env.ns.newUniqueId())
` return await actor.fetch(request)
` }
`}
`export class MyActorClass {
` constructor(state, env) {
` this.name = state.id.name;
` }
` async fetch(request) {
` return new Response(String(this.name));
` }
`}
)
],
bindings = [(name = "ns", durableObjectNamespace = "MyActorClass")],
durableObjectNamespaces = [
( className = "MyActorClass",
uniqueKey = "mykey",
)
],
durableObjectStorage = (inMemory = void)
)
),
],
sockets = [
( name = "main",
address = "test-addr",
service = "hello"
)
]
))"_kj);

test.start();
auto conn = test.connect("test-addr");
conn.httpGet200("/", "undefined");
}

KJ_TEST("Server: Durable Objects retain ctx.id.name for short names") {
TestServer test(R"((
services = [
( name = "hello",
worker = (
compatibilityDate = "2022-08-17",
modules = [
( name = "main.js",
esModule =
`const name = "retained-name-123"
`export default {
` async fetch(request, env) {
` let actor = env.ns.get(env.ns.idFromName(name))
` return await actor.fetch(request)
` }
`}
`export class MyActorClass {
` constructor(state, env) {
` this.name = state.id.name;
` }
` async fetch(request) {
` return new Response(String(this.name));
` }
`}
)
],
bindings = [(name = "ns", durableObjectNamespace = "MyActorClass")],
durableObjectNamespaces = [
( className = "MyActorClass",
uniqueKey = "mykey",
)
],
durableObjectStorage = (inMemory = void)
)
),
],
sockets = [
( name = "main",
address = "test-addr",
service = "hello"
)
]
))"_kj);

test.start();
auto conn = test.connect("test-addr");
conn.httpGet200("/", "retained-name-123");
}

KJ_TEST("Server: Durable Objects drop ctx.id.name for long names") {
TestServer test(R"((
services = [
( name = "hello",
worker = (
compatibilityDate = "2022-08-17",
modules = [
( name = "main.js",
esModule =
`export default {
` async fetch(request, env) {
` let actor = env.ns.get(env.ns.idFromName("a".repeat(1025)))
` return await actor.fetch(request)
` }
`}
`export class MyActorClass {
` constructor(state, env) {
` this.name = state.id.name;
` }
` async fetch(request) {
` return new Response(String(this.name));
` }
`}
)
],
bindings = [(name = "ns", durableObjectNamespace = "MyActorClass")],
durableObjectNamespaces = [
( className = "MyActorClass",
uniqueKey = "mykey",
)
],
durableObjectStorage = (inMemory = void)
)
),
],
sockets = [
( name = "main",
address = "test-addr",
service = "hello"
)
]
))"_kj);

test.start();
auto conn = test.connect("test-addr");
conn.httpGet200("/", "undefined");
}

KJ_TEST("Server: Simultaneous requests to a DO that hasn't started don't cause split brain") {
TestServer test(R"((
services = [
Expand Down
13 changes: 8 additions & 5 deletions src/workerd/server/server.c++
Original file line number Diff line number Diff line change
Expand Up @@ -2277,11 +2277,14 @@ class Server::WorkerService final: public Service,

kj::Own<IoChannelFactory::ActorChannel> getActorChannel(Worker::Actor::Id id) {
KJ_IF_SOME(doId, id.tryGet<kj::Own<ActorIdFactory::ActorId>>()) {
Comment thread
maxmcd marked this conversation as resolved.
// To emulate production, we have to recreate this ID.
ActorIdFactoryImpl::ActorIdImpl* idImpl =
dynamic_cast<ActorIdFactoryImpl::ActorIdImpl*>(doId.get());
KJ_ASSERT(idImpl != nullptr, "Unexpected ActorId type?");
idImpl->clearName();
KJ_IF_SOME(name, doId->getName()) {
// To emulate production, we preserve the name on the id, but only if it's <= 1024 bytes.
if (name.size() > 1024) {
auto* idImpl = dynamic_cast<ActorIdFactoryImpl::ActorIdImpl*>(doId.get());
KJ_ASSERT(idImpl != nullptr, "Unexpected ActorId type?");
idImpl->clearName();
}
}
}

return kj::refcounted<ActorChannelImpl>(getActorContainer(kj::mv(id)));
Expand Down
Loading