Skip to content

Scheduler update#896

Closed
v1r3n wants to merge 25 commits into
mainfrom
scheduler_update
Closed

Scheduler update#896
v1r3n wants to merge 25 commits into
mainfrom
scheduler_update

Conversation

@v1r3n
Copy link
Copy Markdown
Collaborator

@v1r3n v1r3n commented Mar 19, 2026

Pull Request type

  • Bugfix
  • Feature
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • WHOSUSING.md
  • Other (please describe):

NOTE: Please remember to run ./gradlew spotlessApply to fix any format violations.

Changes in this PR

Changes in this PR

Scheduler event field injection

When a workflow is triggered by the scheduler, the workflow's event field is now set to scheduler:. This makes it easy to identify which schedule fired a given workflow execution — visible in
workflow history, the UI, and any downstream observability tooling that reads the event field.

Implementation: added event to StartWorkflowRequest, wired it through the StartWorkflowInput constructor (which already had the field but never populated it from a request), and set it in

SchedulerService.dispatchWorkflow before calling startWorkflow. Test added to AbstractSchedulerServiceIntegrationTest so all persistence backends (Postgres, MySQL, SQLite, Redis) inherit the assertion.

Scheduler examples and scripts

Added scheduler/examples/ with 8 verified workflow + schedule JSON pairs covering common patterns: basic every-minute, catchup mode, bounded schedule, multi-step FORK/JOIN, failure scenario, concurrent
execution, input parameterization, and DO_WHILE internal loop. Includes seed.sh for the Docker demo stack and a README quickstart covering the full API lifecycle, cron format, configuration reference, and
per-scenario run instructions.

Added scheduler/scripts/ with 4 concurrency/load test scripts used during live validation (concurrent UPSERT, concurrent resume, thundering-herd N-schedule fanout, multi-client load blast).

server-lite scheduler wiring

Added conductor-scheduler-sqlite-persistence to server-lite/build.gradle and enabled conductor.scheduler.enabled=true in its application.properties so the standalone lite server supports the scheduler out of
the box.

Issue #

v1r3n and others added 14 commits March 19, 2026 00:51
Covers SQLite module, Redis module (via JedisProxy), and replacement of
all mock-based service tests with Testcontainers-backed integration tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix @ConditionalOnExpression default: scheduler.enabled:false (not true)
- Add JedisProxy extension requirements (zscore, zrevrangeByScore)
- Document truncateStore() hook for Redis test teardown (no DataSource)
- Replace batch hmget/mget with individual hget/get loop (JedisProxy gap)
- Fix removeExecutionRecord to ZREM exec_by_sched via read-first pattern
- Add Redis build.gradle dependencies and settings.gradle registration
- Add META-INF/spring/AutoConfiguration.imports to both new modules
- Add smoke test subclasses to all four modules in the concrete class table
- Clarify @ClassRule Redis container lifecycle for JUnit 4
- Note secondary index omission is intentional for SQLite dev/test scope

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Change SQLite db.type from 'memory' to 'sqlite' (established codebase value)
- Clarify truncateStore() refactoring as explicit prerequisite task, not
  already done — prevents implementer confusion with current base classes
- Mark DST/catchup scenarios as future work (not yet in testFixtures)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers 21 tasks across 4 chunks: prerequisite test-base refactoring,
SQLite module, Redis module (JedisProxy extensions + DAO), and mock
test deletion. All review findings applied:
- @TestConfiguration instead of @configuration in SQLite tests
- FlywayAutoConfiguration added to @ContextConfiguration
- SqliteSchedulerAutoConfigurationSmokeTest unconditionally overrides
  baseRunner() to add hikari.maximum-pool-size=1
- zrevrangeByScore wraps Set<String> in new ArrayList<>() for List return
- deleteWorkflowSchedule does HDEL schedules first to bound inconsistency
- setNextRunTimeInEpoch guards with hexists for non-existent schedules
- JedisProxy construction uses new JedisStandalone(jedisPool) correctly
- Task 20 pre-deletion gate covers all 4 backends (not just 2)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-JDBC test backends

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…chema and tests

Implements a SQLite-backed SchedulerDAO using INSERT OR REPLACE for upserts,
manual IN-placeholder expansion, and a single HikariCP connection (maximumPoolSize=1)
to ensure in-memory database isolation. Includes Flyway migration, Spring Boot
auto-configuration, DAO test, service integration test, and smoke test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… Redis DAO

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…y-backed DAO and tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… backend integration tests

Also add V2 Flyway migrations for MySQL and Postgres (execution_time column +
index), fixing MySQL-incompatible IF NOT EXISTS syntax in ADD COLUMN / CREATE INDEX.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add scheduler/examples/ with 8 verified workflow+schedule JSON pairs
  (basic, catchup, bounded, multistep fork/join, failure, concurrent,
  input-param, do-while) plus seed.sh and a full quickstart README
- Add scheduler/scripts/ with 4 concurrency/load test scripts
  (concurrent write, concurrent resume, thundering herd, load blast)
- Set workflow event field to "scheduler:<name>" on every scheduler-
  triggered workflow via StartWorkflowRequest.event, wired through
  StartWorkflowInput and WorkflowExecutorOps; test added to
  AbstractSchedulerServiceIntegrationTest
- Wire conductor-scheduler-sqlite-persistence into server-lite and
  enable conductor.scheduler.enabled=true so the lite server supports
  the scheduler out of the box

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@v1r3n v1r3n requested a review from nthmost-orkes March 19, 2026 14:48
v1r3n and others added 7 commits March 19, 2026 08:01
- Add getAllExecutionRecords(limit) to SchedulerDAO interface and all 4
  backends (Postgres, MySQL, SQLite, Redis)
- Add searchExecutions() to SchedulerService with in-memory filtering
  by scheduleName, status, workflowType, startTime range, executionId,
  and free-text; supports sort and pagination
- Add GET /api/scheduler/search/executions endpoint to SchedulerResource
  returning {results, totalHits} shape expected by the UI
- Add Scheduler nav entries (Definitions, Executions) to UI sidebar
- Point ui-next dev server at port 7001

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…endpoint

Spring requires explicit parameter names when -parameters compiler flag
is absent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add GET /schedules/search with pagination, freeText, paused, workflowName
  and sort filters — needed by /scheduleDef UI page
- Change pause/resume from PUT to GET (matches Orkes contract and UI fetch)
- Add GET /nextFewSchedules with cronExpression/startTime/endTime/limit params
- Change POST /schedules to return 200 (void) to match Orkes
- Change DELETE /schedules/{name} to return 200 to match Orkes
- searchExecutions now returns SearchResult<WorkflowScheduleExecution>
  instead of raw Map
- Add searchSchedules() and getListOfNextSchedules() to SchedulerService

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tests across all backends

- Add 73 integration tests to AbstractSchedulerServiceIntegrationTest covering:
  - searchSchedules: filter by workflowName, freeText, scheduleName, paused; sort by
    name/createTime/updatedTime ASC/DESC; pagination; combined filters
  - searchExecutions: all 6 query clause types (scheduleName IN, status IN, workflowType IN,
    startTime>/startTime<, executionId); freeText on scheduleName/workflowId/executionId;
    all sort fields (startTime, scheduledTime) with direction defaulting; pagination
  - getListOfNextSchedules: basic, startTime, endTime, and zero-limit cases
- Fix RedisSchedulerDAO.getAllExecutionRecords to use a dedicated KEY_EXEC_SCHED_NAMES set
  instead of relying on getAllSchedules(), so executions saved without a schedule entry are
  correctly enumerated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
}

private boolean matchesQuery(WorkflowScheduleExecution r, String query) {
for (String clause : query.split("(?i)\\s+AND\\s+")) {

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on a
user-provided value
may run slow on strings with many repetitions of ' '.
}
Matcher m;

m = Pattern.compile("(?i)scheduleName\\s+IN\\s+\\(([^)]+)\\)").matcher(clause);

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on a
user-provided value
may run slow on strings starting with 'schedulename in (' and with many repetitions of 'schedulename in (('.
continue;
}

m = Pattern.compile("(?i)status\\s+IN\\s+\\(([^)]+)\\)").matcher(clause);

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on a
user-provided value
may run slow on strings starting with 'status in (' and with many repetitions of 'status in (('.
continue;
}

m = Pattern.compile("(?i)workflowType\\s+IN\\s+\\(([^)]+)\\)").matcher(clause);

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on a
user-provided value
may run slow on strings starting with 'workflowtype in (' and with many repetitions of 'workflowtype in (('.

@PutMapping("/schedules/{name}/pause")
@ResponseStatus(HttpStatus.NO_CONTENT)
@GetMapping("/schedules/{name}/pause")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should be PutMapping


@PutMapping("/schedules/{name}/resume")
@ResponseStatus(HttpStatus.NO_CONTENT)
@GetMapping("/schedules/{name}/resume")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should be PutMapping

schedulerService.pauseSchedule(name);
}
public void pauseSchedule(@PathVariable("name") String name) {
schedulerService.pauseSchedule(name);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Lets add reason parameter back

import org.springframework.ai.openai.api.OpenAiAudioApi;
import org.springframework.ai.openaisdk.OpenAiSdkChatModel;
import org.springframework.ai.openaisdk.OpenAiSdkChatOptions;
import org.springframework.ai.openaisdk.OpenAiSdkEmbeddingModel;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unrelated changes

@v1r3n v1r3n closed this May 2, 2026
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