Skip to content

feat(java): scaler HTTP endpoint for autoscaling#342

Merged
pratyush618 merged 3 commits into
masterfrom
feat/java-scaler
Jul 1, 2026
Merged

feat(java): scaler HTTP endpoint for autoscaling#342
pratyush618 merged 3 commits into
masterfrom
feat/java-scaler

Conversation

@pratyush618

@pratyush618 pratyush618 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

What

Adds an HTTP scaler endpoint to the Java SDK so an external autoscaler (KEDA, HPA external metric) can drive replica count from live queue depth.

  • Scaler (org.byteveda.taskito.scaler) — JDK HttpServer, no new deps.
    • GET /api/scaler{metricValue, targetValue, queueName} where metricValue = pending + running (outstanding work). Optional ?queue=<name> narrows to one queue; otherwise reports across all.
    • GET /health{status:"ok"}.
    • Non-GET → 405; stats failure → 500 with a generic message (never leaks backend internals).
  • ScalerOptions — host/port (0 = ephemeral), optional default queue, targetQueueDepth.
  • Scaler.start(queue, options) binds immediately; close() stops. port() exposes the bound port for port=0.

Mirrors the Python/Node KEDA scaler contract.

Test

ScalerTest covers the endpoint end-to-end against a live queue: depth math, per-queue filtering, health, method rejection.

./gradlew build green (JDK 17 build leg + 21/25 test legs in CI).

Summary by CodeRabbit

  • New Features
    • Added an HTTP scaler service with GET /api/scaler (reports queue depth toward a target) and GET /health (returns status).
    • Added configurable scaler options (host, port, target depth, optional queue) plus easy startup and shutdown controls.
  • Bug Fixes
    • Improved handling of failures when reading queue metrics and invalid request/parameter inputs.
  • Tests
    • Added tests for queue-specific depth reporting, rejecting non-GET requests, health responses, and invalid target-depth validation.
  • Documentation
    • Documented the scaler endpoints and expected behavior.

Serve GET /api/scaler (queue depth = pending + running, plus the target)
and GET /health on a JDK HttpServer, for an external autoscaler (KEDA
metrics-api). Reject a non-positive target depth.
Depth + health over HTTP; non-positive target rejected.
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3974e51a-789e-401f-9822-d1adb0a18223

📥 Commits

Reviewing files that changed from the base of the PR and between 53673c9 and 06a9e0f.

📒 Files selected for processing (1)
  • sdks/java/src/test/java/org/byteveda/taskito/ScalerTest.java

📝 Walkthrough

Walkthrough

Adds a new Java Scaler HTTP service for autoscaling, with configurable options, /api/scaler and /health endpoints, package documentation, and tests covering queue depth reporting, queue selection, request validation, and option validation.

Changes

Scaler HTTP Service

Layer / File(s) Summary
ScalerOptions configuration record
sdks/java/src/main/java/org/byteveda/taskito/scaler/ScalerOptions.java
Adds ScalerOptions with target-depth validation, host normalization, and defaults() / onPort(int) factories.
Scaler HTTP server implementation
sdks/java/src/main/java/org/byteveda/taskito/scaler/package-info.java, sdks/java/src/main/java/org/byteveda/taskito/scaler/Scaler.java
Adds the embedded server, /api/scaler and /health handlers, queue-depth JSON responses, query parsing, error handling, lifecycle methods, and package-level API documentation.
Scaler test coverage
sdks/java/src/test/java/org/byteveda/taskito/ScalerTest.java
Adds tests for queue-depth reporting, queue filtering, GET-only rejection, health responses, and invalid target-depth construction.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Scaler
  participant Taskito

  Client->>Scaler: GET /api/scaler?queue=name
  Scaler->>Taskito: getQueueStats(queue)
  Taskito-->>Scaler: pending, running
  Scaler-->>Client: JSON metricValue, targetValue, queueName

  Client->>Scaler: GET /health
  Scaler-->>Client: JSON status ok
Loading

Poem

A bunny built a scaler bright,
To count the queues and serve them right.
Hop to /health, hop to /api/scaler,
Depths and status in tidy sailor.
The hare approves this HTTP flight 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a Java scaler HTTP endpoint for autoscaling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/java-scaler

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (4)
sdks/java/src/main/java/org/byteveda/taskito/scaler/Scaler.java (4)

82-94: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Query parameter value is not URL-decoded.

If a queue name contains characters requiring percent-encoding (spaces, &, =, etc.), the raw substring returned here won't match the actual queue name, silently breaking the ?queue= filter.

🔧 Proposed fix
+import java.net.URLDecoder;
 ...
             if (eq > 0 && pair.substring(0, eq).equals(key)) {
-                return pair.substring(eq + 1);
+                return URLDecoder.decode(pair.substring(eq + 1), StandardCharsets.UTF_8);
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/java/src/main/java/org/byteveda/taskito/scaler/Scaler.java` around lines
82 - 94, The queryParam(HttpExchange, String) helper currently returns the raw
query substring, so values for the queue filter are not URL-decoded and encoded
names won’t match. Update this method to decode the extracted value before
returning it, and handle decoding safely for the queue lookup path used by
Scaler so query parameters like queue names with spaces or reserved characters
are matched correctly.

50-53: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Abrupt shutdown via stop(0).

server.stop(0) closes immediately without waiting for in-flight exchanges to complete, which can truncate a response mid-request during close(). A small grace delay (e.g., stop(1)) would allow outstanding requests to finish.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/java/src/main/java/org/byteveda/taskito/scaler/Scaler.java` around lines
50 - 53, The Scaler.close() implementation is stopping the server immediately
with server.stop(0), which can cut off in-flight requests; update the close()
method on Scaler to use a small grace period so outstanding exchanges can finish
before shutdown. Keep the change localized to the server.stop call in close(),
preserving the existing lifecycle behavior while allowing a brief drain window.

72-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Failure reason is swallowed without any diagnostics.

Catching RuntimeException and returning a generic 500 is correct for not leaking internals to the caller, but discarding e entirely makes production issues (bad queue name, backend errors) hard to diagnose. Consider logging e (or its message) server-side before responding.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/java/src/main/java/org/byteveda/taskito/scaler/Scaler.java` around lines
72 - 75, The RuntimeException handling in Scaler should still return the generic
500 to callers, but it must not swallow the failure silently. Update the catch
block in Scaler’s queue-stats path to log the caught exception e server-side
before calling send(exchange, 500, ...), so backend issues like bad queue names
or storage failures can be diagnosed without exposing internals to the client.

32-43: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Default bind host 0.0.0.0 exposes queue metrics broadly with no auth.

HttpServer.create uses options.host(), which defaults to 0.0.0.0 in ScalerOptions. Combined with no authentication on /api/scaler, this means anyone with network reachability to the pod/host can read queue depth. This may be intentional for cluster-internal KEDA scraping, but worth confirming binding scope matches deployment expectations (e.g., default to loopback unless explicitly widened).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/java/src/main/java/org/byteveda/taskito/scaler/Scaler.java` around lines
32 - 43, The scaler endpoint in Scaler.start currently binds HttpServer.create
to ScalerOptions.host(), which defaults to 0.0.0.0 and exposes /api/scaler
without authentication. Update ScalerOptions (and any callers/config defaults)
so the default bind host is loopback or otherwise restricted, and only widen it
when explicitly configured. Keep the server setup in Scaler.start aligned with
the intended deployment scope by using the host value intentionally rather than
exposing queue metrics by default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdks/java/src/test/java/org/byteveda/taskito/ScalerTest.java`:
- Around line 20-58: The ScalerTest coverage is missing the per-queue filtering
and non-GET rejection cases described by the PR. Add tests in ScalerTest that
exercise Scaler.handleScaler through the HTTP endpoint with a ?queue=<name>
query to verify queue-specific metricValue, and another test that sends a
non-GET request to the scaler endpoint and asserts the 405 response. Keep the
existing reportsQueueDepthAndHealth and rejectsNonPositiveTarget tests, and use
the existing Scaler/ScalerOptions setup and HttpClient helper to locate and
validate the behavior.

---

Nitpick comments:
In `@sdks/java/src/main/java/org/byteveda/taskito/scaler/Scaler.java`:
- Around line 82-94: The queryParam(HttpExchange, String) helper currently
returns the raw query substring, so values for the queue filter are not
URL-decoded and encoded names won’t match. Update this method to decode the
extracted value before returning it, and handle decoding safely for the queue
lookup path used by Scaler so query parameters like queue names with spaces or
reserved characters are matched correctly.
- Around line 50-53: The Scaler.close() implementation is stopping the server
immediately with server.stop(0), which can cut off in-flight requests; update
the close() method on Scaler to use a small grace period so outstanding
exchanges can finish before shutdown. Keep the change localized to the
server.stop call in close(), preserving the existing lifecycle behavior while
allowing a brief drain window.
- Around line 72-75: The RuntimeException handling in Scaler should still return
the generic 500 to callers, but it must not swallow the failure silently. Update
the catch block in Scaler’s queue-stats path to log the caught exception e
server-side before calling send(exchange, 500, ...), so backend issues like bad
queue names or storage failures can be diagnosed without exposing internals to
the client.
- Around line 32-43: The scaler endpoint in Scaler.start currently binds
HttpServer.create to ScalerOptions.host(), which defaults to 0.0.0.0 and exposes
/api/scaler without authentication. Update ScalerOptions (and any callers/config
defaults) so the default bind host is loopback or otherwise restricted, and only
widen it when explicitly configured. Keep the server setup in Scaler.start
aligned with the intended deployment scope by using the host value intentionally
rather than exposing queue metrics by default.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f206f0b1-f437-4556-940c-ab9362bad84b

📥 Commits

Reviewing files that changed from the base of the PR and between f8596b0 and 53673c9.

📒 Files selected for processing (4)
  • sdks/java/src/main/java/org/byteveda/taskito/scaler/Scaler.java
  • sdks/java/src/main/java/org/byteveda/taskito/scaler/ScalerOptions.java
  • sdks/java/src/main/java/org/byteveda/taskito/scaler/package-info.java
  • sdks/java/src/test/java/org/byteveda/taskito/ScalerTest.java

Comment thread sdks/java/src/test/java/org/byteveda/taskito/ScalerTest.java
@pratyush618 pratyush618 merged commit 270f856 into master Jul 1, 2026
19 checks passed
@pratyush618 pratyush618 deleted the feat/java-scaler branch July 1, 2026 03:07
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.

1 participant