Skip to content

Conversation

@majiayu000
Copy link
Contributor

Summary

  • Add built-in internal scheduler for self-hosted environments
  • Scheduler polls /api/schedules/execute to trigger scheduled workflows
  • Enabled by default in docker-compose.prod.yml for self-hosted setups
  • Configurable via ENABLE_INTERNAL_SCHEDULER, CRON_SECRET, and INTERNAL_SCHEDULER_INTERVAL_MS environment variables

Test plan

  • Added unit tests for internal scheduler
  • All tests pass (4 tests)
  • Manual testing with Docker deployment

Fixes #1870

@vercel
Copy link

vercel bot commented Dec 22, 2025

@majiayu000 is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 22, 2025

Greptile Summary

This PR adds a built-in scheduler for self-hosted environments to trigger scheduled workflows by polling /api/schedules/execute at configurable intervals (default 60 seconds). The scheduler is initialized via Next.js instrumentation hooks and enabled by default in Docker deployments.

Major Changes:

  • Created internal-scheduler.ts with polling logic, authentication, and graceful shutdown handling
  • Integrated scheduler initialization in instrumentation-node.ts register function
  • Added ENABLE_INTERNAL_SCHEDULER, CRON_SECRET, and INTERNAL_SCHEDULER_INTERVAL_MS environment variables
  • Configured Docker Compose with scheduler enabled by default
  • Enhanced Helm network policies to support custom egress rules for scheduler traffic
  • Comprehensive unit tests with 4 test cases covering authentication and error handling

Issues Found:

  • Insecure default CRON_SECRET in docker-compose.prod.yml allows unauthorized access
  • NaN polling interval when INTERNAL_SCHEDULER_INTERVAL_MS is undefined
  • Localhost fallback won't work in containerized environments
  • Signal handlers registered multiple times on hot reloads could cause memory leaks
  • Silent initialization failures may leave users unaware that scheduled workflows aren't running

Confidence Score: 3/5

  • This PR has several critical bugs that will cause runtime failures, particularly around NaN interval handling and localhost fallback in containers
  • Score reflects multiple logic bugs that will cause failures: NaN interval will cause runaway polling, localhost fallback breaks in containers, insecure default credentials, and signal handler leaks. The implementation approach is sound but needs fixes before merge.
  • Pay close attention to apps/sim/lib/scheduler/internal-scheduler.ts for the NaN interval bug and localhost fallback issue, and docker-compose.prod.yml for the insecure default credentials

Important Files Changed

Filename Overview
apps/sim/lib/scheduler/internal-scheduler.ts Added internal scheduler that polls /api/schedules/execute at configurable intervals for self-hosted deployments
apps/sim/instrumentation-node.ts Integrated internal scheduler initialization in the Node.js instrumentation entry point
apps/sim/lib/core/config/env.ts Added environment variable definitions for ENABLE_INTERNAL_SCHEDULER and INTERNAL_SCHEDULER_INTERVAL_MS
docker-compose.prod.yml Configured internal scheduler environment variables with defaults, including insecure default CRON_SECRET

Sequence Diagram

sequenceDiagram
    participant App as Next.js App
    participant Instrumentation as instrumentation-node.ts
    participant Scheduler as Internal Scheduler
    participant API as /api/schedules/execute
    participant DB as Database
    participant Workflow as Workflow Executor

    Note over App,Instrumentation: Application Startup
    App->>Instrumentation: register()
    Instrumentation->>Scheduler: initializeInternalScheduler()
    
    alt ENABLE_INTERNAL_SCHEDULER=true
        Scheduler->>Scheduler: Check CRON_SECRET exists
        Scheduler->>Scheduler: startInternalScheduler()
        Scheduler->>Scheduler: Set isRunning flag
        Scheduler->>API: GET /api/schedules/execute<br/>(Bearer token auth)
        Note over Scheduler,API: Immediate first poll
        
        loop Every INTERNAL_SCHEDULER_INTERVAL_MS (default: 60s)
            Scheduler->>Scheduler: Check if previous poll running
            alt Previous poll still running
                Scheduler->>Scheduler: Skip this cycle
            else Can proceed
                Scheduler->>Scheduler: Set isRunning = true
                Scheduler->>API: GET /api/schedules/execute<br/>(Authorization: Bearer CRON_SECRET)
                API->>API: verifyCronAuth()
                API->>DB: Query due schedules<br/>(nextRunAt <= now)
                DB-->>API: Return due schedules
                API->>DB: Update lastQueuedAt
                
                loop For each due schedule
                    API->>Workflow: Execute workflow
                end
                
                API-->>Scheduler: Return {executedCount: N}
                Scheduler->>Scheduler: Set isRunning = false
                
                alt executedCount > 0
                    Scheduler->>Scheduler: Log executed count
                end
            end
        end
    else ENABLE_INTERNAL_SCHEDULER=false
        Note over Scheduler: Scheduler disabled,<br/>relies on external cron
    end
    
    Note over App,Scheduler: Graceful Shutdown
    App->>Scheduler: SIGTERM/SIGINT
    Scheduler->>Scheduler: stopInternalScheduler()
    Scheduler->>Scheduler: clearInterval()
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (5)

  1. apps/sim/lib/core/config/env.ts, line 128-129 (link)

    style: Use z.boolean() instead of z.string() for boolean environment variables

    For consistency with other boolean flags in this file (like DISABLE_REGISTRATION, BILLING_ENABLED, TRIGGER_DEV_ENABLED), use proper boolean type:

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

  2. apps/sim/lib/scheduler/internal-scheduler.ts, line 126-133 (link)

    style: Multiple signal handlers registered on every initialization

    If initializeInternalScheduler() is called multiple times (e.g., in hot reload or tests), this adds duplicate SIGTERM/SIGINT handlers. Consider checking if handlers already exist or using process.once():

  3. apps/sim/lib/scheduler/internal-scheduler.ts, line 79 (link)

    style: Handle invalid INTERNAL_SCHEDULER_INTERVAL_MS values

    Number() returns NaN for invalid strings. Consider validating or using parseInt with error handling:

  4. docker-compose.prod.yml, line 11-26 (link)

    logic: Missing required INTERNAL_API_SECRET environment variable

    The app requires INTERNAL_API_SECRET (min 32 chars) to start, as defined in apps/sim/lib/core/config/env.ts:28. Add it after ENCRYPTION_KEY.

  5. apps/sim/lib/scheduler/internal-scheduler.ts, line 106-107 (link)

    style: If ENABLE_INTERNAL_SCHEDULER is changed to z.boolean() in env.ts (as suggested in another comment), update comparison from === 'true' to === true

6 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

@waleedlatif1 waleedlatif1 deleted the branch simstudioai:staging December 27, 2025 05:25
@waleedlatif1 waleedlatif1 reopened this Dec 27, 2025
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (5)

  1. apps/sim/lib/scheduler/internal-scheduler.ts, line 127-133 (link)

    logic: multiple registrations of SIGTERM and SIGINT handlers will occur if initializeInternalScheduler() is called multiple times (e.g., during hot reloads in development). This could lead to memory leaks or unexpected behavior.

  2. apps/sim/lib/scheduler/internal-scheduler.ts, line 34 (link)

    logic: fallback to http://localhost:3000 will not work correctly in containerized environments where the app might not be accessible on localhost. The scheduler is running inside the container and needs to call its own HTTP endpoint.

  3. apps/sim/lib/scheduler/internal-scheduler.ts, line 79 (link)

    logic: Number(undefined) returns NaN, which will cause setInterval to fire repeatedly without delay

  4. docker-compose.prod.yml, line 25 (link)

    logic: the default value default-cron-secret-change-me is insecure and could allow unauthorized access to scheduled workflows. Remove the default or use an empty string to force users to set their own secret.

  5. apps/sim/instrumentation-node.ts, line 119-125 (link)

    style: caught errors are only logged. If scheduler initialization fails silently, scheduled workflows won't run and users may not notice. Consider adding startup health checks or more visible warnings.

6 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

Lutherwaves and others added 3 commits December 27, 2025 16:12
…dioai#2481)

The realtime service network policy was missing the custom egress rules section
that allows configuration of additional egress rules via values.yaml. This caused
the realtime pods to be unable to connect to external databases (e.g., PostgreSQL
on port 5432) when using external database configurations.

The app network policy already had this section, but the realtime network policy
was missing it, creating an inconsistency and preventing the realtime service
from accessing external databases configured via networkPolicy.egress values.

This fix adds the same custom egress rules template section to the realtime
network policy, matching the app network policy behavior and allowing users to
configure database connectivity via values.yaml.
Adds built-in scheduler that periodically polls /api/schedules/execute
to trigger scheduled workflows in self-hosted environments.

Enable by setting ENABLE_INTERNAL_SCHEDULER=true (enabled by default in
docker-compose.prod.yml). Also requires CRON_SECRET to be configured.

Fixes simstudioai#1870
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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