move @cloudflare/containers into cloudflare:workers#5247
Conversation
e03a9f9 to
d26d41b
Compare
|
The generated output of |
CodSpeed Performance ReportMerging #5247 will not alter performanceComparing Summary
Footnotes
|
70ceead to
e503c34
Compare
9e1712e to
bf35d6e
Compare
|
|
||
| jsg::JsObject self(args.This()); | ||
| self.set(js, "ctx", jsg::JsValue(args[0])); | ||
| self.set(js, "env", jsg::JsValue(args[1])); |
There was a problem hiding this comment.
This signature feels... off. You're passing in the ctx and env as arguments but then using the args to get them instead. You're also grabbing the jsg::Lock& using jsg:Lock::from(...) when it can just be acquired by setting it as the first argument accepted by the constructor.
There was a problem hiding this comment.
To be totally honest i've just ripped this off the entrypoints directly above, which does say this bit is a hack 🤷 - I'm not very sure what I'm doing here, and how much /what of ContainerEntrypoint should be implemented here in the c++ part. It's basically just a special DO class I want to export from the workers module and it should ideally inherit as much as possible from DOs...
There was a problem hiding this comment.
No worries, just doing an early scan through, mostly to get familiar with the case you're enabling. Feel free to ignore these early comments :-)
There was a problem hiding this comment.
not at all, any reviews on this is super appreciated, please let me know if i'm barrelling down the wrong path 😅
| if (!FeatureFlags::get(js).getContainerEntrypoint()) { | ||
| KJ_FAIL_REQUIRE( | ||
| "ContainerEntrypoint is an experimental feature and requires the 'container_entrypoint' compatibility flag."); | ||
| } |
There was a problem hiding this comment.
| if (!FeatureFlags::get(js).getContainerEntrypoint()) { | |
| KJ_FAIL_REQUIRE( | |
| "ContainerEntrypoint is an experimental feature and requires the 'container_entrypoint' compatibility flag."); | |
| } | |
| KJ_REQUIRE(FeatureFlags::get(js).getContainerEntrypoint(), | |
| "ContainerEntrypoint is an experimental feature and " | |
| "requires the 'container_entrypoint' compatibility flag."); |
There was a problem hiding this comment.
i've ended up removing all the code here - is there a way of accessing compat flags from the typescript implementation?
8130439 to
8ff0be4
Compare
e7c2c33 to
ab21c2c
Compare
ab21c2c to
a5b7294
Compare
| } | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
| ctx.blockConcurrencyWhile(async () => { |
There was a problem hiding this comment.
nit: void ctx.blockConcurrencyWhile(...)
There was a problem hiding this comment.
might be able to remove the eslint ignore too
| ); | ||
| }); | ||
|
|
||
| this.container = ctx.container; |
There was a problem hiding this comment.
I'd move above the ctx.blockConcurrencyWhile so the setInactivityTimeout call does not need the ? operator on the ctx.container
| const NOT_LISTENING_ERROR = 'the container is not listening'; | ||
|
|
||
| function isErrorOfType(e: unknown, matchingString: string): boolean { | ||
| const errorString = e instanceof Error ? e.message : String(e); |
There was a problem hiding this comment.
you can now use Error.isError() method
| userProvidedSignal: AbortSignal | undefined, | ||
| timeoutMs: number | ||
| ): AbortSignal { | ||
| const controller = new AbortController(); |
There was a problem hiding this comment.
You can use AbortSignal.timeout
| // we are out of retries | ||
| if (attempt === options.retries.limit - 1) { | ||
| if ( | ||
| e instanceof Error && |
There was a problem hiding this comment.
Error.isError is preferred
danlapid
left a comment
There was a problem hiding this comment.
Hold with merging until I fully review please.
kentonv
left a comment
There was a problem hiding this comment.
I am hard against this change in its current form:
Immediate concrete objection
Putting this code into cloudflare:workers will slow down isolate startup for everyone who imports cloudflare:workers even if they don't use containers. cloudflare:workers is needed by almost all applications. A huge containers library doesn't belong in this module.
This objection can be solved by placing the library into its own, separate module.
Broader philosophical objection
Once this lands in the runtime, we have to support it forever. We can't make backwards-incompatible changes at all.
But, this library presents an opinionated framework that is far higher-level than anything we've put into runtime APIs in the past. At the very least, the runtime team would need to carefully review these opinions to decide if we're comfortable with locking them in. Briefly looking at the docs, I already spot some design decisions that I am not very comfortable with -- but I won't go into them here.
Moreover, every change to this library going forward will require review from the runtime team. They will need to be deployed as a runtime release, which is slower that updating a library. Breakages may require rolling back the runtime release, which will not just slow down the containers team but also everyone else trying to land changes in the runtime.
I don't think either the Runtime team or the Container team really wants this.
Alternative direction
If I understand correctly, what the containers team really wants is the ability to update this library without requiring every customer to redeploy their worker to pick up the update.
I think we could create a new mechanism for accomplishing exactly that. We could create a notion of "field-updatable modules". These modules would not be embedded in the runtime itself, but rather would live in nearby storage, where the runtime can fetch them on-demand while loading a worker. Using separate storage would decouple releases from runtime releases, and would allow us to support a large number of libraries deployed this way, such that the runtime team doesn't feel like they are picking winning opinions.
But I think even if we do this, it should be an internal detail of the containers library. That is, app developers should still perceive that they are importing @cloudflare/containers. But that package itself would contain a simple wrapper that imports the field-updatable module and then re-exports it.
This way, you can still make breaking changes to the API in the future, by changing the code in the npm package to point at a different field-updatable module name, which implements the new API. The old name still points at a perfectly backwards-compatible version of the library, to avoid breaking deployed users.
This allows people to import the containers class (currently here https://github.com/cloudflare/containers and published to npm) from
cloudflare:workersinstead.ContainerEntrypoint should extend DurableObject and inherit everything from that, with some additional container-specific functionality.
I anticipate there will be some bikeshedding on the name.
Testing