Skip to content

feat(samples): add schedule_sample with full lifecycle demo#136

Open
abhishekj720 wants to merge 2 commits into
mainfrom
feat/schedules-samples
Open

feat(samples): add schedule_sample with full lifecycle demo#136
abhishekj720 wants to merge 2 commits into
mainfrom
feat/schedules-samples

Conversation

@abhishekj720

@abhishekj720 abhishekj720 commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds samples/schedule_sample.py — a two-subcommand script (worker + demo) that walks through the complete Cadence Schedules lifecycle: create → describe → pause → unpause → backfill → update → list → delete
  • Adds samples/README.md cataloguing the sample with run instructions, following the cadence-samples Go repo pattern
  • Updates the main README.md with a short "Samples" pointer to samples/README.md
  • Demo wraps the lifecycle in try/finally so the schedule is cleaned up and the client closed even if an intermediate step raises

Test plan

  • uv run python samples/schedule_sample.py worker — starts worker cleanly
  • uv run python samples/schedule_sample.py demo — runs full lifecycle, prints each step
  • uv tool run ruff check samples/ — no lint errors

Comment thread samples/schedule_sample.py
Comment thread samples/schedule_sample.py Outdated
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@abhishekj720 abhishekj720 force-pushed the feat/schedules-samples branch from 905f3bd to 45226d8 Compare June 13, 2026 00:58
Comment thread samples/schedule_stress/schedule_stress.py Outdated
Comment thread samples/schedule_stress/schedule_stress.py Outdated
@abhishekj720 abhishekj720 force-pushed the feat/schedules-samples branch 2 times, most recently from 525f6a5 to 54330f4 Compare June 13, 2026 01:02
Comment thread samples/schedule_sample.py Outdated
Adds samples/schedule_sample/schedule_sample.py — a two-subcommand
script (worker + demo) that walks through the complete schedule
lifecycle: create → describe → pause → unpause → backfill → update →
list → delete.

Also adds samples/README.md cataloguing the sample with run
instructions, and updates the main README to point to it.

The demo wraps the lifecycle in try/finally so the schedule is cleaned
up and the client closed even if an intermediate step raises.

Signed-off-by: abhishek.jha <abhishek.jha@uber.com>
@abhishekj720 abhishekj720 force-pushed the feat/schedules-samples branch from 54330f4 to 28926fe Compare June 13, 2026 01:05
Co-authored-by: Abhishek Jha  <17800780+abhishekj720@users.noreply.github.com>
@gitar-bot

gitar-bot Bot commented Jun 13, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 5 resolved / 5 findings

Adds comprehensive schedules sample and stress testing utilities, resolving client leakage, error handling, and output discrepancies. No open issues found.

✅ 5 resolved
Edge Case: Cleanup delete in except can mask the original error

📄 samples/schedule_sample/schedule_sample.py:82-96
In run_demo, the except Exception: handler calls await client.delete_schedule(sid) and then raise. If the failure originated from create_schedule (e.g. the schedule was never created, the domain lacks scheduler support, or a connection error), the delete_schedule call will itself most likely raise (NotFound / connection error). That new exception propagates out of the except block before raise runs, masking the original root-cause exception that the user actually needs to see for debugging. Wrap the cleanup delete in its own try/except (best-effort) so the original error is preserved.

Quality: Backfill comment/output claims 2 triggers but window yields ~120

📄 samples/schedule_sample/schedule_sample.py:109-118
The backfill section comments # --- backfill (2 triggers in the last 2 hours) --- and prints Backfill : submitted 2-hour window. At the time of backfill the schedule still uses the per-minute cron * * * * * (the cron is only changed to hourly later in the update step). A 2-hour backfill of a per-minute schedule produces roughly 120 triggers, not 2, so the comment is misleading for someone learning the API from this sample. Either change the comment to reflect the actual ~120 triggers (or switch to an hourly window for 2 triggers), to avoid teaching an incorrect mental model.

Quality: schedule_stress: Client never closed, leaking gRPC channels

📄 samples/schedule_stress/schedule_stress.py:140-153 📄 samples/schedule_stress/schedule_stress.py:156-160 📄 samples/schedule_stress/schedule_stress.py:189-191 📄 samples/schedule_stress/schedule_stress.py:212-214 📄 samples/schedule_stress/schedule_stress.py:219-221 📄 samples/schedule_stress/schedule_stress.py:229-231 📄 samples/schedule_stress/schedule_stress.py:258-260 📄 samples/schedule_stress/schedule_stress.py:265-267 📄 samples/schedule_stress/schedule_stress.py:123-137 📄 samples/schedule_stress/schedule_stress.py:156-170 📄 samples/schedule_stress/schedule_stress.py:189-203 📄 samples/schedule_stress/schedule_stress.py:212-216 📄 samples/schedule_stress/schedule_stress.py:219-226 📄 samples/schedule_stress/schedule_stress.py:229-243 📄 samples/schedule_stress/schedule_stress.py:258-262 📄 samples/schedule_stress/schedule_stress.py:265-269
In samples/schedule_stress/schedule_stress.py none of the commands close the Client they create with _build_client. The gRPC channel is left open until process exit. This is most visible in cmd_create_one, whose try/finally: pass block (L150-152) has a comment "gRPC channel cleanup" but does nothing — dead/misleading code. The same applies to cmd_create_many, cmd_backfill, cmd_describe, cmd_list, cmd_cleanup, cmd_pause, cmd_unpause. The Client class supports an async context manager (__aenter__/__aexit__) and await client.close(), so the idiomatic fix is to wrap usage in async with _build_client(args) as client: (as the sister sample schedule_sample.py does with client.close() in a finally). For short-lived CLI invocations this is low impact, but the empty finally is misleading and the worker/long-running paths benefit from clean shutdown.

Quality: schedule_stress main does not catch KeyboardInterrupt

📄 samples/schedule_stress/schedule_stress.py:360-367 📄 samples/schedule_stress/schedule_stress.py:130-137
In samples/schedule_stress/schedule_stress.py, cmd_worker documents "worker running; ctrl-c to stop" and catches asyncio.CancelledError, but main calls asyncio.run(args.func(args)) without catching KeyboardInterrupt. When the user presses Ctrl-C, asyncio.run cancels the task (handled internally) and then re-raises KeyboardInterrupt, which propagates out of main/sys.exit(main()) and prints a traceback — contradicting the "ctrl-c to stop" guidance. The sibling sample samples/schedule_sample.py already handles this by wrapping asyncio.run in try/except KeyboardInterrupt. Mirror that here for a clean exit.

Quality: schedule_sample worker leaks client (never closed)

📄 samples/schedule_sample.py:52-58
run_worker creates a Client but never closes it. The Worker async context manager closes only the worker's tasks, not the client's gRPC channel (cadence/worker/_worker.py:47-62), so the channel is leaked when the worker exits on Ctrl-C. The run_demo path correctly closes the client in a finally; the worker path should do the same for consistency and to avoid 'channel not closed' warnings in this sample.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

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.

2 participants