fix: enforce per-task push notification config limit - #1044
Conversation
| * Prevents a single task from accumulating an unbounded list of configs | ||
| * (each config consumes memory and can trigger outbound HTTP requests). | ||
| */ | ||
| public static final int MAX_PUSH_CONFIGS_PER_TASK = 100; |
There was a problem hiding this comment.
This should be made configurable via A2AConfigProvider and a2a-defaults.properties. And it will need adding to the docs
| } | ||
| notificationConfig = builder.build(); | ||
|
|
||
| // Enforce the per-task limit (BUG-42). Re-registering/updating an already-registered |
… in the JPA store - Add a2a.push-notification-config.max-per-task (default 100) to a2a-defaults.properties, read via A2AConfigProvider. - Add PushNotificationConfigStore.maxPushConfigsPerTask() helper and document the per-task limit in the interface javadoc. - InMemoryPushNotificationConfigStore now reads the configured limit (null-safe for direct construction in tests). - JpaDatabasePushNotificationConfigStore enforces the same limit before persisting a new config.
|
Thanks for the review — all three points are addressed:
Testing: |
Add the `a2a.push-notification-config.max-per-task` property to the configuration docs page, complementing PR a2aproject#1044 which introduces per-task push notification config limits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Thanks for adding the missing configuration documentation — merged. The |
|
No problem — thanks for moving it to the dev docs; merged. Makes sense since the property isn't in the 1.2.0 release yet. |
kabir
left a comment
There was a problem hiding this comment.
Thanks for addressing all three items from the first review — JPA store enforcement, interface javadoc, and configurability + docs all look good.
Things that look solid:
maxPushConfigsPerTask()is well-designed — null-safe for non-CDI usage, handles bad input gracefully- The "update existing config at limit" edge case is handled correctly in both stores
- Tests cover the three key scenarios (at limit, exceeding, update at limit)
- Docs placed in
docs/content/dev/(correct location) - Limit check happens before insert in both stores, so the store stays unchanged on rejection
Two suggestions:
-
No JPA-side test for the limit enforcement. The in-memory store has 3 tests for the limit, but
JpaDatabasePushNotificationConfigStoreIntegrationTestdoesn't exercise the new limit path. If the count query has a typo or the>=comparison is wrong, there's no test catching it. A test that registers configs up to the limit and verifies the next one throwsInvalidParamsErrorwould close this gap. -
Minor: magic number
100repeated 3 times inmaxPushConfigsPerTask()(PushNotificationConfigStore.java). The constantMAX_PUSH_CONFIGS_PER_TASKexists onInMemoryPushNotificationConfigStorebut can't be referenced from the interface without a reverse dependency. Consider definingDEFAULT_MAX_PUSH_CONFIGS_PER_TASKon the interface itself and using it in both places.
…tant - Define DEFAULT_MAX_PUSH_CONFIGS_PER_TASK on PushNotificationConfigStore and use it in maxPushConfigsPerTask() (removes the repeated magic 100); InMemoryPushNotificationConfigStore.MAX_PUSH_CONFIGS_PER_TASK now references it. - Add JpaDatabasePushNotificationConfigStoreIntegrationTest coverage for the per-task limit: registering up to the limit succeeds, the next distinct config throws InvalidParamsError, and updating an existing config ID at the limit is still allowed.
847257f to
8622f32
Compare
Restore the documentation for a2a.push-notification-config.max-per-task in docs/content/dev/configuration.md (previously contributed via the fork PR that was inadvertently dropped during a force-push).
|
Both suggestions addressed:
Testing: JPA integration 21 passed (incl. the new limit test); InMemory 32 passed. The |
Rename `config` field to `configProvider` to avoid shadowing by local variables of the same name in setInfo and deleteInfo. Combine the existing-config check and the remove-old-config loop into a single `removeIf` call, eliminating a redundant iteration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Hi, I opened ez-lbz#13 with the remainder of the PR feedback |
refactor: simplify setInfo limit check and fix field shadowing
|
Merged the refactor (#13) — thanks for the field-shadowing fix and the single-pass limit check. |
|
Thanks @ez-lbz! BTW it seems we have a new job installed at org level which checks if commits are signed off. See https://github.com/a2aproject/a2a-java/pull/1044/checks?check_run_id=93965391402. It is fine for now, and I have not been doing this myself either, but will start now. So please do this for new work, the PRs in the queue are probably fine. Also, you might want to use your SSH key to sign the commits as outlined in here https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification#ssh-commit-signature-verification this is so that they show as 'Verified' when you look at the commit history in GitHub :-) |
What changed
1. Enforce a per-task push notification config limit
Problem:
InMemoryPushNotificationConfigStore.setInfomaintained an unboundedList<TaskPushNotificationConfig>per task. A client could register unlimited push notification configs for one task; each config consumes memory and can trigger an outbound HTTP request on every task event, enabling resource exhaustion and amplified outbound traffic.Fix (server-common/src/main/java/org/a2aproject/sdk/server/tasks/InMemoryPushNotificationConfigStore.java):
MAX_PUSH_CONFIGS_PER_TASK = 100(matching the Python SDK's cap).setInfonow counts only new configs against the limit: re-registering/updating an already-registered config ID remains allowed. When a genuinely new config would exceed the limit,setInfothrowsInvalidParamsError(code -32602), the same error family already used for push-config validation, which transports surface as a client error.Fix (server-common/src/test/java/org/a2aproject/sdk/server/tasks/InMemoryPushNotificationConfigStoreTest.java):
testSetInfoAtLimitExactlyAllowed— the 100th config still succeeds.testSetInfoRejectsExceedingPerTaskLimit— the 101st distinct config throwsInvalidParamsErrorand the store is unchanged.testSetInfoUpdateExistingConfigAtLimitAllowed— updating an existing config at the limit is still allowed.Behavior change: registering more than 100 distinct push notification configs for a single task now fails with
InvalidParamsErrorinstead of succeeding. Updates to existing configs are unaffected.2. Enforce the limit in the JPA store and make it configurable
Problem: The per-task limit only existed in the in-memory store, leaving
JpaDatabasePushNotificationConfigStoreunbounded; the limit was also hardcoded rather than operator-configurable.Fix (server-common + extras/push-notification-config-store-database-jpa):
a2a.push-notification-config.max-per-task(default 100) toMETA-INF/a2a-defaults.properties, read throughA2AConfigProvider.PushNotificationConfigStore.maxPushConfigsPerTask(A2AConfigProvider)and documented the per-task limit in the interface javadoc.InMemoryPushNotificationConfigStorereads the configured limit (null-safe when constructed directly).JpaDatabasePushNotificationConfigStorecounts existing configs for the task before persisting a new one and rejects beyond the limit withInvalidParamsError.Testing
mvn -pl server-common test -Dtest=InMemoryPushNotificationConfigStoreTest— 32 tests, 0 failures.mvn -pl extras/push-notification-config-store-database-jpa test— 35 tests, 0 failures, 1 skipped (pre-existing).