Backend: Spring-style beans, transactions, scheduling, metrics and MCP, resolved at build time - #5908
shai-almog wants to merge 50 commits into
Conversation
…P, resolved at build time The backend gains Spring's programming model under com.codename1.backend.annotations -- @Service/@Component/@repository, @Autowired, @value, @ConfigurationProperties, @bean, scopes, profiles and conditions, @transactional, @async, @scheduled, @ManagedResource/@Timed/@counted and @mcptool -- with every decision made by the build: a generated BackendWiring constructs and injects the beans with plain code, and aspects are woven into the annotated methods. No container, scan, proxy or reflection at run time. Runtime: thread-bound transactions that the pool, daos and sessions join, a cron and fixed-rate scheduler with an optional database lock, task executors including fd-less virtual-thread tasks with a per-host wake pipe, HTTP sessions (memory or JDBC store), OTLP metrics and management endpoints, and an MCP endpoint with development tools. Also fixes the sticky virtual-thread yield reason (yieldNow from a handler hung), a handler taking HttpServer.Request being refused, and adds the backend and full-stack references to the generated agent skill. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6dd51f4064
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Compared 172 screenshots: 172 matched. Benchmark ResultsDetailed Performance Metrics
ParparVM vs HotSpot (JDK 25): Windows x64Runner CPU: AMD64 Family 25 Model 1 Stepping 1, AuthenticAMD (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A regression is a ratio more than 15% (time) / 15% (RAM) above its baseline in
Result: no regression |
|
Compared 172 screenshots: 172 matched. Benchmark ResultsDetailed Performance Metrics
|
Cloudflare Preview
|
|
Compared 172 screenshots: 172 matched. ParparVM vs HotSpot (JDK 25): Linux x64Runner CPU: AMD EPYC 9V74 80-Core Processor (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A regression is a ratio more than 15% (time) / 15% (RAM) above its baseline in
Result: no regression |
|
Compared 172 screenshots: 172 matched. ParparVM vs HotSpot (JDK 25): Linux arm64Runner CPU: Neoverse-N2 (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A regression is a ratio more than 15% (time) / 15% (RAM) above its baseline in
Result: no regression |
|
Compared 172 screenshots: 172 matched. Benchmark ResultsDetailed Performance Metrics
ParparVM vs HotSpot (JDK 25): Windows arm64Runner CPU: ARMv8 (64-bit) Family 8 Model D49 Revision 0, MICROSOFT CORPORATION (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A regression is a ratio more than 15% (time) / 15% (RAM) above its baseline in
Result: no regression |
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
✅ ByteCodeTranslator Quality ReportTest & Coverage
Benchmark Results
Static Analysis
Generated automatically by the PR CI workflow. |
|
Compared 193 screenshots: 193 matched. |
- Sessions: the session cookie goes on a copy of the handler's Response, never into it (it may be a shared constant); cn1.session.secure refuses anything but auto/true/false. - TaskExecutor: virtual submissions are refused after shutdown and counted active before the hand-off; @async accessors re-fetch a shut-down executor. - MCP: only loopback or listed origins pass (the Host match let DNS rebinding through); byte/short tool arguments are range-checked; backend_call uses https against a TLS server. - Request metrics are recorded in a finally, so failures count and the route label is cleared. - Factory beans inherit their configuration class's @Profile and @ConditionalOnProperty; request-scoped beans' destroyMethod runs. - Scheduler lock: an INSERT failure with no existing row is rethrown. - OTLP histogram bucket_counts stays fixed64 (per metrics.proto), now held by a test decoding with the generated opentelemetry-proto classes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Compared 157 screenshots: 157 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
|
Compared 150 screenshots: 150 matched. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e31da12f35
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Compared 155 screenshots: 155 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
…lit the guide Review fixes: - Sessions, MCP tools and managed beans are per server (Backend.getSessions, Environment.registerTool/registerManaged, Builder.mcpTool); two servers in one process no longer share cookies, sessions or tools. - @SessionScope beans are kept by the server for the session's lifetime and destroyed on invalidate, expiry and stop; a negative session timeout fails. - Transactions: no process-wide default pool; a NESTED method before the first statement sets its savepoint after BEGIN. setRollbackOnly in the method that began the transaction rolls back without throwing. - Backend.stop runs destroy callbacks once; a failed start destroys built beans; management routes precede application handlers; session-store failures are logged as 500s; metrics shutdown is bounded by its timeout. - Weaving keeps synchronized on the body, so a synchronized @async method holds its monitor where it runs. - Injection points and lifecycle methods inherited from base classes are wired; @ConditionalOnMissingBean matches the bean's exposed types and takes explicit ones. Docs: the backend chapter is split into nine chapters (web, beans, data and transactions, sessions, scheduling, observability, MCP, operations) with diagrams and compiled samples. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Compared 223 screenshots: 223 matched. |
|
Compared 166 screenshots: 166 matched. Benchmark Results
Detailed Performance Metrics
ParparVM vs HotSpot (JDK 25): macOS arm64Runner CPU: Apple M1 (Virtual) (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A regression is a ratio more than 15% (time) / 15% (RAM) above its baseline in
Result: no regression |
|
Compared 155 screenshots: 155 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 28cad305bb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Compared 154 screenshots: 154 matched. Benchmark Results
Detailed Performance Metrics
|
- Executors belong to the server that opened them: request, task, scheduler and start-up threads carry it, stopping one server no longer shuts down another's, and generated @async code looks the executor up per call. At the shutdown deadline queued tasks are dropped rather than run against destroyed beans, and running ones are interrupted; a virtual task a host cannot run falls back to its own executor. - Every server applies cn1.session.* (handler-only ones sent a TLS session cookie without Secure); a failing request still stores its session so its session beans are kept or destroyed. - Request beans are destroyed while their request is still current; @PreDestroy runs subclass before superclass; factory beans run inherited lifecycle methods. - Health reports STARTING until the start-up hook returns. - A cron time inside a DST gap is skipped instead of firing an hour late. - The metrics exporter can be reopened; histograms copy and validate their bounds and labels. - A managed resource must be a singleton and cannot overload operations. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d4d761e707
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
CodenameOne/vm/backend/src/com/codename1/backend/otel/OtlpTracer.java
Lines 208 to 210 in c821e65
When two live builders are given the same OtlpTracer, the second open() replaces this instance's exporter while the first export thread remains alive. Spans from both servers then enqueue through the new exporter, and stopping either backend shuts that exporter down; Tracing also records the shared tracer only once in its owned set, so the other live server loses tracing. Refuse an already-open tracer instance, or maintain independent per-open state rather than overwriting the live fields.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…lly, interface aspects refused - The memory store answers a lookup only when the session still has that id: a login's changeSessionId() retires the old, possibly planted id at once, as a servlet container does, instead of when the login request saves. An undone rotation restores the id and the entry answers again. - Request-scoped beans that a @PreDestroy builds are destroyed however deep the chain goes (bounded), not just one pass further. - @transactional, @async, @timed or @counted on a project interface -- on a default method, an abstract one, or the type -- is a build error: the build weaves classes, and the annotation was silently applied to nothing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 035be3064b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…s in Spring - Backend.start() refuses a second live Backend in the same process (a stopping one is waited for, so stop-then-start still works). Scaling out is more processes -- sessions in the JDBC store, job locks in the database -- and the runtime's process-wide state (tracer, metrics and exporter, default executors, virtual-thread hosts) assumes one server. The tests that exercised two concurrent servers are removed; one test pins the rule. - A required dependency whose candidates are all conditional is checked at start, as Spring checks it (Wiring.single names the injection point); the developer guide no longer claims the build proves coverage. - A @scheduled method returning a Future builds with a warning: its return is ignored, as in Spring, and the run ends when the method returns. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b45f82f5d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… names, full termination; build on master - Moved method bodies are named per declaring class, so a same-package subclass's woven override no longer overrides the base class's body: an explicit super.foo() runs the base body instead of recursing. - stop() called from a virtual @async task no longer waits out the drain for its own host: each virtual task records the host it runs on. - Two beans of one class with @scheduled methods (two @bean factories) get their jobs named by bean, as Spring schedules each, instead of refusing the start with a duplicate name. - awaitTermination() also waits for a deferred teardown, so a process does not exit while @PreDestroy and the pool close are still running. - The wake-pipe native reads the array payload through CN1_ARRAY_DATA: master's 4-byte header (#5903) removed the data pointer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
…yTest flake check-ablation-arms swept CN1_HAVE_DLL_INTRINSICS as a -D arm, but like CN1_HAVE_SB_INTRINSICS it is defined by cn1_intrinsics.h when the translation carries dart_core_DartLongList.h, and forcing it without that header cannot compile. TelemetryTest: the same change as 98e65a7 on backend-spring-model (#5908). An earlier test's queued export reached the collector mock after the connections were cleared, and the traceparent test accepted any span. Applied identically so the two branches merge cleanly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3acab79911
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…start cursor, slot released - Woven body names carry the declaring class's own name, not its hash, and support-class names encode `_` and `$` injectively: two classes whose names shared a hash (Aa, BB), or Outer_Inner and Outer$Inner, no longer get one name -- a recursing super call, or one helper replacing the other. - A file response replaced by a 500 because the session could not be stored closes its descriptor; only the writer closed it before. - The virtual-task host cursor is reduced to the current server's host count, so a restart with fewer hosts does not index past its array. - A stopped Backend frees the process slot, so its destroyed beans and sessions are not kept reachable until the next start. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f930488ec3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… runs synchronously - Support-class names escape `_` as `__` and `$` as `_S`, so no two class names meet (A$_B and A_$B collided under the previous scheme). - Scoped and lazy stand-ins are named per bean: two @bean methods of one class, both request-scoped, each get their own instead of one source replacing the other. - A stand-in whose bean's constructor calls an overridable method runs the bean's own code during construction rather than dereferencing a scope that is not assigned yet. - @async on @PostConstruct or @PreDestroy is not applied, with a warning, as Spring calls lifecycle methods on the bean itself: the server no longer becomes ready before initialisation, and a @PreDestroy body still runs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 030a2feef3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…tate fault twin may wedge Review: - A request's scoped beans are destroyed even when serialising the deferred JSON body throws: the array was taken first, and every later pass found nothing to destroy. - @Profile("!") (an empty negated name) is a build error; it activated the bean under every profile. - A @PostConstruct or @PreDestroy returning a Future builds with a warning: the return is ignored, as in Spring, and the server goes on when the method returns. Gates: - perf-baseline.json: the windows-x64 AMD family 25 model 1 rows are recalibrated with calibrate-perf-baseline.py from the six calibration runs of #5903 plus this branch's run, which changes nothing in the VM. Its arrayRandom rounds spanned 1.06-1.49 inside one run (median 1.38 against a 0.97 baseline) -- runner noise the row's 25% tolerance could not absorb; the row is now 1.061 with 45%, the script's own rule for the observed spread. - GcSteadyStateIntegrationTest scenario 4 builds the reserve OUT (-DCN1_PACING_NO_RESERVE) to prove scenario 3 can fail. That deliberately broken build can wedge on the admission margin instead of finishing, and the gate required it to finish, so it failed on a slow runner (footprint 745MB of a 768MB budget, no cycles, no reserve parks). A wedge is now accepted as the demonstration, checked from the per-second probe: headroom under the threshold and no reserve parks. A finishing run is held to the same assertions as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… session store - Remove every hand-wired server from the guide (ServerSnippets, raw HttpServer.start/serveForever, installByHand, mcpTool builder); the build writes the entry point and that is the only supported shape. - Promote java.util.concurrent Future/ExecutionException/TimeoutException/ CancellationException/TimeUnit (and Properties) to documented backend API: rendered in the backend javadoc, gated by check-backend-jdk-surface.py (which also scans the guide's demos), and exercised on ParparVM by SelfTest.futures(). - Session store value "jdbc" -> "db"; diagrams clarified (no bare "null", fixedRate vs fixedDelay with varied run lengths). - ParparVM virtual threads are not Java 21 ones; say so. - Correct doc claims the audit found false: CN1_ prefixed env names, multi-server passages (one backend per process), @scheduled scope rules, management "always on in dev", injected DataSource instead of DataSource.open(getenv), PushFeedback on the backend Json and injection. - Document the missing cn1.otel.* keys and CN1_HTTP_MAX_UPLOAD_MB. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ations - Backend no longer names Management or McpServer. Only the builder's management() and mcp() do, and the generated entry point calls them only when the build asked (@enablemanagement / @EnableMcpServer / an @mcptool / a literal cn1.*.enabled=true in any application*.properties, or a dev build). The translator drops the uncalled builder method and the classes with it; BackendOtelTest now asserts from nm that a server that never asked carries neither, against a control that links both. - Typed settings annotations -- @serverconfig, @SessionConfig, @DataSourceConfig, @StaticFilesConfig, exporter settings on @opentelemetry, paths on the Enable* ones -- compiled in as the bottom configuration layer (Config.withCompiledDefaults), under the files and the environment. Values the runtime would refuse, and two classes disagreeing, are build errors naming the class. - OtlpTracerTest.partialSuccessIsCounted waited on a flush that could run before the request's span was queued (the span ends after the response is written); it now waits for the span first. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…as JSON A route may now return an entity or DTO (and collections or String-keyed maps of them) and take one as @RequestBody, as a Spring controller does through Jackson. The build writes a <Name>Cn1Json codec per class -- plain code, no reflection -- in the app's @mapped JSON form, so a class shared by app and backend round-trips: fields (public directly, others via bean accessors), @JsonProperty/@JsonIgnore (now shared with the backend), Date as epoch millis (read from millis or ISO-8601), byte[] base64, enums by name, subclasses written as themselves, unknown members ignored. - A body the codec refuses is a 400 naming the path ($.lines[0].quantity: expected ...); a response nesting past 64 objects (a cycle) is a 500 pointing at @JsonIgnore. - Generic type-variable fields, body classes without a no-arg constructor, interfaces, arrays other than byte[], and runtime types such as HttpServer.Response inside a list stay build errors, with the reason. - HttpServer renders a deferred JSON body before the write, into the same buffer, so a throw there (a Writable, a codec) is answered 500 and recorded as the handler's failure instead of dropping the connection unanswered. - The guide's order example is compiled from the guide's own files and run end to end by BackendBeansTest. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…host A virtual thread used to park only on the socket it served. An outbound read -- a PostgreSQL or MySQL query, a Web call, a TLS handshake -- did a blocking recv() on the host pthread, and with one host per core that many slow calls stopped the server answering anything. - Native: a per-virtual-thread wait record (descriptors, events, timeout) and cn1BackendVtWait/cn1BackendAwaitFd; resume answers WAITING (3) for it. Outbound Tcp descriptors stay non-blocking for life and every wait (connect, read, write, TLS handshake/read/write) parks on a virtual thread and polls anywhere else, so platform threads behave as before. SO_RCVTIMEO/SO_SNDTIMEO become the wait's deadline. Web drives libcurl through the multi interface on a virtual thread. - Host: WAITING registers the descriptors with the host's own poller for exactly the wait, wakes on readiness or deadline, and the stop drain keeps pumping tasks that wait on outbound I/O. - Docs/javadoc/skill: network databases, Web and TLS are fine on VIRTUAL; SQLite, file access, host-name resolution and Object.wait still block. - Tests: SelfTest (Tcp, Web, TLS handshake, TLS read, read deadline) and DbCheck (pg_sleep / SLEEP against a one-host server) prove another request is served while the call waits; both fail with the old blocking wait. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The seven AvoidUsingHardCodedIP findings are loopback and wildcard literals that recognise or bind the local interface and dial nothing; each carries a //NOPMD with that reason, the convention ServerSocket and Credentials already use. The OwnRoute overrides get @OverRide. - SelfTest exercises JsonCodec on the translated runtime: range and fraction refusals with their paths, dates from millis and ISO-8601 with offsets and fractions, an impossible date refused, base64, writeDate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 903a18fb09
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…and paths - An Object or raw Map/List field is written by its run-time class through a generated cn1app.JsonValues: a class the build writes goes through its codec, Date as millis, enums by name, JDK shapes through Json; any other class is a 500 instead of its toString(). - A TreeSet body needs a Comparable element; refused at build time otherwise. - readLong bounds a fractional-free double by 2^63 before converting, so 9223372036854775808.0 is refused instead of clamped to Long.MAX_VALUE. - MCP and management match the canonical path (pathFrom), as the routers, static files and relay do: /%6dcp is /mcp. - MCP answers an allowed origin's CORS preflight before authentication and names the origin on every reply to it (new Response.header). - The guide's order example uses one source block per include, which the snippet validator requires. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8a415127f0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return pool.execute("DELETE FROM " + TABLE + " WHERE max_inactive > 0 AND " | ||
| + "(last_accessed + max_inactive * 1250.0 < ? OR " | ||
| + "last_accessed + max_inactive * 1000.0 + " + TOUCH_INTERVAL + " < ?) " | ||
| + "AND namespace = ?" + spare.toString(), params.toArray()); |
There was a problem hiding this comment.
Protect sessions active on another replica from purge
When replicas share this DB store, busy contains only sessions used by the current process. A request on replica A can load a session near the end of its expiry grace and continue running while replica B executes this delete; once the remaining grace elapses, B removes the row even though A is still using it. A's later save() sees no row and returns without restoring it, so its changes are lost and the client's next request is signed out. The DB store needs a cross-replica lease/touch strategy rather than relying solely on the local busy exclusion.
Useful? React with 👍 / 👎.
| if (ok) { | ||
| bean.tools.add(tool); | ||
| } |
There was a problem hiding this comment.
Validate MCP tool return values before registration
When an @McpTool returns an ordinary application DTO, or a container holding one, this accepts the method after validating only its parameters. McpServer.callTool() subsequently passes the value to Json.write(), whose fallback serializes unknown objects as a quoted toString() value such as "com.example.Order@...", so the agent receives bogus text instead of the promised JSON object. Reject unsupported return shapes here or route them through the generated DTO codecs.
Useful? React with 👍 / 👎.
| Database opened; | ||
| try { | ||
| opened = Database.open(url); |
There was a problem hiding this comment.
Open pooled connections outside the pool monitor
On the packaged virtual-thread server, an on-demand Database.open() can now park during TCP connect, authentication, or setup, but borrowFromPool() is synchronized and therefore retains the data-source monitor across this call. The host may then run another virtual request that tries to borrow from the same pool and blocks the OS host acquiring that monitor; because virtual threads have host affinity, the parked opener cannot resume on another host to release it, and concurrent requests can wedge every host on the same monitor. Reserve pool capacity under the lock and perform the blocking/parking open outside it; the exhausted-pool wait() path likewise needs a cooperative virtual-thread wait.
Useful? React with 👍 / 👎.
| if (!toolArgument(args[i])) { | ||
| ctx.error(cls, "Parameter " + (i + 1) + " of @McpTool " + where + " is a " | ||
| + args[i].getClassName() + "; a tool argument is a String, a " | ||
| + "number, a boolean, an enum, a Map or a List."); |
There was a problem hiding this comment.
Validate generic MCP collection element types
When a tool parameter is declared as List<Integer>, Map<String, MyDto>, or another parameterized collection, this check sees only the erased ASM type and accepts it. The generated adapter then returns the parser's raw collection unchanged; JSON integers are Long values and JSON objects are Map values, so the tool body encounters ClassCastException when it reads the declared elements and otherwise-valid calls can never succeed. Inspect the generic parameter signature and recursively convert supported elements, or reject shapes the adapter cannot materialize.
Useful? React with 👍 / 👎.
| long now = databaseNow(); | ||
| long until = AsyncTask.deadline(now, job.lockAtMostFor); | ||
| int updated = locks.execute("UPDATE " + LOCK_TABLE + " SET lock_until = ?, locked_at = ?, " | ||
| + "locked_by = ? WHERE name = ? AND lock_until <= ?", | ||
| new Object[] {Long.valueOf(until), Long.valueOf(now), lease, job.lock, Long.valueOf(now)}); |
There was a problem hiding this comment.
Start scheduler leases when the claim is written
When a replica is delayed after databaseNow()—for example while waiting for a pool connection or an UPDATE lock—and that delay reaches lockAtMostFor, the later UPDATE or INSERT stores an already-expired lock_until but this method still returns a lease. Another replica can immediately claim the same row while the first proceeds into the job, so a job can overlap before its body has even consumed its configured lease. Derive the expiry from database time in the claim statement or transaction, or reject a claim whose lease is no longer live.
Useful? React with 👍 / 👎.
What
The backend now reads like a Spring application --
@Service,@Autowired,@Value,@ConfigurationProperties,@Bean, scopes,@Profile/@ConditionalOnProperty,@Transactional,@Async,@Scheduled,@ManagedResource/@Timed/@Counted,@McpTool-- all undercom.codename1.backend.annotations, and all resolved at build time:BackendBeanAnnotationProcessorresolves the bean graph and the entry point's generatedBackendWiringconstructs and injects every bean with straight-linenew/setter calls. Private@Autowiredfields get a woven setter. Missing, ambiguous or cyclic dependencies are build errors naming the injection point.@Transactional,@Async,@Timed,@Countedrewrite the method itself (body moved aside, the method delegates to a generated helper) -- no proxy, so self-calls, private methods andnew-built objects get them too.Transactions(all seven propagations, savepoints, read-only) thatDataSource, daos and managed sessions join;Scheduler(cron masks computed by the build, fixed rate/delay, DB lock across replicas);Tasks/TaskExecutorincluding fd-less virtual-thread tasks with a per-host wake pipe;HttpSessionwith memory and JDBC stores; OTLP metrics +/manageendpoints (health, JSON, Prometheus, jobs, managed beans); an MCP endpoint serving@McpToolmethods and, on dev profiles, development tools (backend_routes,backend_beans,backend_call,backend_requests,backend_sql, ...), excluded fromcn1:backend-packageby default.backend.mdandfull-stack-loop.mdreferences in the Initializr skill, archetype/Initializr backend sample with an injected service, new Backend guide sections with compiled snippets.Fixes found on the way
yieldNow()from a handler hung and IO parks after GC backpressure were misreported as runnable; each yield site now sets its reason.Future.get()on a virtual thread blocked the host and could wedge every host; it now yields cooperatively.HttpServer.Requestwas refused ($vs.in the nested type name).Verification
vm/testsbackend suites (HTTP 72/72, WebSocket, OTel, runtime self-test, DB/ORM on all three engines) pass; a sample packaged withcn1:backend-packageexercised every feature and survived a 7000-request concurrent soak.Known limits
Aspects apply to project classes only; request/session/lazy beans need a non-final class with a no-arg constructor (run once for the stand-in); no transaction isolation levels; session-bean
@PreDestroynot run on expiry;cn1app-archetype-test.shextended but not run locally.🤖 Generated with Claude Code