Skip to content

feat: add async-config-poll sample (async-egress engine demo)#146

Merged
Aditya-eddy merged 2 commits into
keploy:mainfrom
Aditya-eddy:add-async-config-poll
Jul 16, 2026
Merged

feat: add async-config-poll sample (async-egress engine demo)#146
Aditya-eddy merged 2 commits into
keploy:mainfrom
Aditya-eddy:add-async-config-poll

Conversation

@Aditya-eddy

Copy link
Copy Markdown
Member

What

Adds async-config-poll/: a Spring Boot 1.5 / Java 8 rule-engine sample that demonstrates Keploy's async-egress engine.

The app has two MySQL-backed HTTP endpoints and, in the background, long-polls a central config service for version changes:

Interaction When Keploy treats it as
GET /v1/buckets/app-common, app-features, app-config?watch=false once, at boot (blocking) ordinary sync mocks
GET /v1/buckets/app-config?watch=true&version=N forever, from a daemon thread async egress (lane config-watch)
MySQL SELECT ... per request ordinary sync mocks

The background watch poll runs on its own timer, so it does not line up one-to-one with the ingress testcases and the version param changes every poll. The async lane in keploy.yml tells Keploy to serve those polls independently of testcase ordering and to treat version as shape-noise, so replay stays green and the app's poller stays happy.

Endpoints

  • GET /health — small payload; runs SELECT 1 against MySQL.
  • GET /rules/{useCase} — ordered rules for (useCase, tenant) from MySQL. Requires X-Tenant-Id + X-Agent-Id. Example: GET /rules/ORDER_FLOW.

Contents

Self-contained: pom.xml, sources under com.example.asyncconfig, docker-compose.yml (MySQL 5.7), init.sql seed, a Go config-stub for the config service, and keploy.yml declaring the async lane. See the sample README.md.

Notes

A Spring Boot 1.5 / Java 8 rule engine (MySQL-backed) that also long-polls a
central config service in the background (GET /v1/buckets/app-config?watch=true).
That watch poll fires from a daemon thread on the app's own schedule — async
relative to the ingress testcases — so Keploy records and replays it through the
async-egress engine (lane "config-watch" in keploy.yml, matched on watch=true,
version treated as volatile).

Endpoints: GET /health (SELECT 1) and GET /rules/{useCase} (rules read from
MySQL). Ships docker-compose (MySQL 5.7), init.sql seed, a Go config-service
stub, and keploy.yml declaring the async lane.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Aditya Sharma <aditya282003@gmail.com>
Copilot AI review requested due to automatic review settings July 16, 2026 06:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new self-contained sample app (async-config-poll/) demonstrating Keploy’s async-egress engine via a Spring Boot (1.5) / Java 8 rule-engine service that boot-fetches config and then long-polls config changes in a background daemon thread.

Changes:

  • Introduces a Spring Boot + Jersey service with /health and /rules/{useCase} endpoints backed by MySQL.
  • Adds a background config watcher (ConfigWatchService) that long-polls app-config and is intended to be replayed via Keploy async egress (lane config-watch).
  • Adds local-run assets: MySQL docker compose + seed SQL, a Go config-stub, and keploy.yml with async lane configuration.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
async-config-poll/src/main/resources/application.yml App/server settings, datasource config, and tunables for the poll interval and artificial rule delay.
async-config-poll/src/main/java/com/example/asyncconfig/rules/UseCaseRules.java DTO for grouped rules response payload.
async-config-poll/src/main/java/com/example/asyncconfig/rules/RulesResource.java Jersey resource implementing GET /rules/{useCase}.
async-config-poll/src/main/java/com/example/asyncconfig/rules/RuleDao.java JDBC data access for rules and their actions.
async-config-poll/src/main/java/com/example/asyncconfig/rules/RuleAction.java DTO for rule actions in the response.
async-config-poll/src/main/java/com/example/asyncconfig/rules/Rule.java DTO for individual rules in the response.
async-config-poll/src/main/java/com/example/asyncconfig/rules/HealthResource.java Jersey resource implementing GET /health with a deterministic payload and a MySQL SELECT 1.
async-config-poll/src/main/java/com/example/asyncconfig/rest/JerseyConfig.java Registers Jersey resources (/health, /rules).
async-config-poll/src/main/java/com/example/asyncconfig/config/ConfigWatchService.java Boot-time config fetch + background long-poll watcher intended for async-egress replay.
async-config-poll/src/main/java/com/example/asyncconfig/Application.java Spring Boot application entrypoint.
async-config-poll/README.md Sample documentation and local record/replay instructions.
async-config-poll/pom.xml Maven project definition for Spring Boot 1.5 + Jersey + JDBC + MySQL connector.
async-config-poll/keploy.yml Declares Keploy async lane (config-watch) with volatile query param handling.
async-config-poll/init.sql MySQL schema and seed data for the rule engine demo.
async-config-poll/docker-compose.yml Local MySQL 5.7 service with seeded init script.
async-config-poll/config-stub/main.go Go stub for the config service endpoints used at record time.
async-config-poll/config-stub/go.mod Go module definition for the stub.
async-config-poll/.gitignore Ignores build outputs, Keploy artifacts, and stub binary/logs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

* Rule-engine endpoint: GET /rules/{useCase}. Requires the X-Tenant-Id and
* X-Agent-Id headers and returns the ordered rules for the (useCase, tenant),
* read from MySQL:
* [{"use_case","tenant","rules":[{"rule_id","constraints","actions":[...],"rule_type"}]}]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f5d608e — the Javadoc now shows a valid JSON example inside a <pre> block.

Comment on lines +58 to +60
List<UseCaseRules> result = ruleDao.rulesFor(useCase, tenantId);
String json = mapper.writeValueAsString(result);
return Response.ok(json).build();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method is annotated @Produces("application/json;charset=utf-8"), so JAX-RS sets that as the response Content-Type even though the entity is a String — Response.ok(String) inherits the resource method's single @produces value. Verified against a recorded response: Content-Type: application/json;charset=utf-8. No change needed.

Comment on lines +79 to +82
if (v > appConfigVersion) {
appConfigVersion = v;
log.info("config watch: app-config advanced to version {}", v);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f5d608e — demoted to log.debug. The poller runs forever and the stub advances the version on every poll, so info would flood normal runs.

Comment on lines +83 to +87
} catch (Exception e) {
// At replay the async engine keep-alives when nothing is
// armed; a failed poll is non-fatal to the running app.
log.debug("config watch poll failed: {}", e.getMessage());
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f5d608e — now log.debug("config watch poll failed", e), so the stack trace is available when DEBUG is enabled.

Comment on lines +105 to +108
throw new IllegalStateException(
"ConfigWatchService: failed to fetch config bucket '" + name
+ "' from " + url + " — application cannot boot", e);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f5d608e — the message now adds an actionable hint: "Ensure the config service is reachable and that app.config.baseUrl points at it."

Comment on lines +33 to +39
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"name": name,
"version": version,
"keys": map[string]string{
"feature.enabled": "true",
},
})

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f5d608e — ConfigWatchService now reads feature.enabled from the bucket's nested keys map (via a boolFromKeys helper), matching the config-stub's response shape. Previously the top-level read always returned null and silently fell back to the default.

Comment on lines +49 to +51

return Response.ok(mapper.writeValueAsString(body)).build();
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the /rules thread: the method's @Produces("application/json;charset=UTF-8") sets the Content-Type for the String entity. Verified against the recorded /health response: Content-Type: application/json;charset=utf-8. No change needed.

Aditya-eddy added a commit to keploy/keploy that referenced this pull request Jul 16, 2026
Add a Java-on-Linux CI job that exercises the async-egress engine end to end
against the async-config-poll sample (keploy/samples-java): record the /health
and /rules HTTP tests plus the MySQL + config-service egress, then replay with
the real deps down and assert both the ingress tests PASSED and the engine's
`async egress verdict` (served >= 1, shape_flags == 0 — the volatile "version"
watch param matches cleanly).

- New reusable-workflow job `async_config_poll` in java_linux.yml, modelled on
  the existing tidb/mysql jobs (same guard, download-binary, checkout
  samples-java, upload artifacts). Deviations, each commented: Temurin 8 (the
  sample is Spring Boot 1.5), an added setup-go step for the config stub, and a
  single build/build matrix cell — the released binary can neither stamp nor
  serve async mocks, so the *_latest cells could never validate the feature
  (mirrors tidb-stmt-cache's omission comment).
- New run script test_workflow_scripts/java/async_config_poll/java-linux.sh:
  brings up MySQL (docker compose) with a TCP readiness probe, builds+runs the
  Go config stub, records, then replays and checks the report + async verdict.
  Deliberately does not source update-java.sh (which pins Java 17).

The sample app lives in keploy/samples-java#146; this job goes green once that
merges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Aditya Sharma <aditya282003@gmail.com>
- ConfigWatchService: read feature.enabled from the bucket's nested "keys" map
  (matching the config-stub's response shape) instead of the top level, where
  it never resolved.
- ConfigWatchService: demote the per-poll "version advanced" log to DEBUG (the
  poller runs forever and the version can advance every poll), and log the poll
  exception object so a stack trace is available under DEBUG.
- ConfigWatchService: make the boot-failure message actionable (check the
  config service is reachable / app.config.baseUrl is correct).
- RulesResource: make the response-shape Javadoc example valid JSON.

Content-Type feedback on /health and /rules is already handled by each method's
@produces("application/json...") (recorded responses carry application/json), so
no change there.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Aditya Sharma <aditya282003@gmail.com>
@Aditya-eddy
Aditya-eddy merged commit d981643 into keploy:main Jul 16, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants