Files
kenjreno ca1243c396 v0.1.0: consolidate intermediate tags into single fresh release
Wipes v0.1.0/v0.1.1 history.  fpc-cron hadn't shipped to any
external consumer yet; the rebuild against fpc-db v0.4.0 was
inline development noise.  This commit + tag is the clean public
starting point.

CRON_VERSION reset to 0.1.0.  CHANGELOG rewritten as a single
0.1.0 entry covering the full library surface as it stands now.
2026-05-06 11:32:25 -07:00

145 lines
5.6 KiB
Markdown

# Changelog
All notable changes to fpc-cron are recorded here. Every
release tag (`vX.Y.Z`) points at the commit that bumped
`CRON_VERSION` in `src/cron.version.pas`. Downstream consumers
should pin by tag, not commit hash.
Semver intent:
- **major** — breaks callers (API removal, signature change)
- **minor** — additive features
- **patch** — bug fixes, internal cleanups
## 0.1.0 — 2026-05-06
Initial public release. Cron + interval task runner extracted
from canonical Fastway `TFWScheduler` (`fw_scheduler.pas`,
1093 lines) per `feedback_copy_dont_reinterpret.md`. Cron
parser bodies are byte-identical to canonical after the
type-rename normalisation.
### Added
- `cron.runner.TCron` — task runner thread, public surface:
- `Create(APool, ARunTask=nil, AGetExtraTasks=nil, ALogger=nil)`
— constructs in suspended state; declares schema, loads
tasks, runs `SyncPluginTasks`.
- `RegisterSystemTask(AName, AProc)` — register a callback
for a `system/<name>` task (replaces canonical's
hardcoded case-table).
- `RefreshTasks` / `RunTaskNow(ATaskID)`.
- `GetTasksJSON: TJSONArray` / `GetTaskJSON(ATaskID): TJSONObject`.
- `UpdateTask(ATaskID, AUpdates): Boolean` — apply enabled /
schedule_type / interval_seconds / cron_expr changes,
persists to DB.
- `Running: Boolean`.
- `class function ParseCronField(AField, AMin, AMax): TBits`
- `class function MatchesCron(ACronExpr, ATime): Boolean`
- Typed observer properties: `OnTaskStart` / `OnTaskComplete`
/ `OnTaskRegistered` / `OnPluginOrphaned` / `OnThreadStart`
/ `OnThreadStop`.
- `cron.types``TCronTaskKind`, `TCronTask`, `TRunTaskProc`,
`TGetExtraTasksFunc`, `TSystemTaskProc`.
- `cron.events` — typed observer callback types
(`TCronOnTaskStart`, ...) following the bp.events / cm.events
pattern.
- `cron.runner.BuildSystemSchedulerSpec(ANowExpr): TDBTable`
and `BuildSchedulerLogSpec: TDBTable``database.TDBTable`
specs mirroring canonical Fastway `fw_schema.pas`'s
`BuildSystemScheduler` / `BuildSchedulerLog`.
- `cron.version``CRON_VERSION` semver constants.
### Adjustments from canonical (the only behaviour-preserving
changes)
| Canonical | fpc-cron |
|------------------------|------------------------|
| `TFWScheduler` | `TCron` |
| `TFWSchedulerTask` | `TCronTask` |
| `TFWSchedulerTaskKind` | `TCronTaskKind` |
| `TRunPluginTaskProc` | `TRunTaskProc` |
| `TGetPluginTasksFunc` | `TGetExtraTasksFunc` |
- `uses` clause swaps: drops `fw_consts` / `fw_log` / `fw_config`
/ `fw_database` / `fw_plugin_api` / `fw_plugin_updates` /
`fw_plugin_host` / `dbapi_consts` / `dbapi_dialect`; adds
`log.types` (fpc-log), `database.dialect` / `database.schema`
/ `database.pool` (fpc-db v0.1.0), `cron.types` /
`cron.events`.
- Global `DB` replaced with `TDBPool` parameter on `Create`.
- `EventBus.Fire(...)` calls replaced with typed observer
callbacks (assignable as properties on `TCron`). Same
per-library typed-callback pattern as fpc-binkp's
`bp.events` and fpc-comet's `cm.events`.
- `fw_log.Log.X(...)` calls routed through optional
`log.types.TLogProc` callback with `Category='cron'`.
- `DoWalCheckpointTruncate` (SQLite + Fastway specific) and
the hardcoded `case ATaskName` system-task table lifted out
— consumers register their own system tasks via
`RegisterSystemTask`.
- No global `Scheduler` singleton — consumer creates own.
- `ParseCronField` / `MatchesCron` promoted from private
instance methods to `class function` for testability;
bodies byte-verbatim from canonical.
### Behaviours preserved verbatim (per the COPY rule)
- `LoadTasksFromDB` / `SyncPluginTasks` / `UpdateTaskInDB` /
`LogTaskRun` hit the database directly.
- 5-position cron parser (`*`, `*/N`, `a,b,c`, `a-b`, `a-b/N`,
literal).
- Cron expressions in *local* time; UTC at storage time.
- `UTCNow = LocalTimeToUniversal(Now)` for every persisted
timestamp.
- Schedule-miss-on-long-task: next firing is delay AFTER
previous run *returned*.
- TThread + PRTLEvent + 1-second wake loop.
- Suspended-Create + `.Start`.
- SyncPluginTasks orphan cleanup.
- Event names through bus bridge: `'scheduler.task_start'` and
`'scheduler.task_result'` (canonical literals).
- DB table names `system_scheduler` and `scheduler_log` kept
verbatim (Fastway database is reusable as-is).
### Known issues inherited verbatim
- `MatchesCron` allocates the five `TBits` field-bit-arrays
before its `try`/`finally`; if `ParseCronField` raises on
field 2..5, earlier `TBits` instances leak. Negligible
real-world impact.
- `MatchesCron` calls `DecodeDateFully(ATime, Mn, Hr, Dom, Dow)`
whose results are immediately overwritten by the next four
calls. Cosmetic.
### Tests
73 assertions across 23 scenarios:
- `tests/test_cron.pas` — 37 pure-cron assertions.
- `tests/test_runner.pas` — 36 SQLite-backed end-to-end:
Init, NextRun-recalc, RunTaskNow, SyncPluginTasks add +
orphan, IntervalFires, SystemTaskFires,
SystemTaskFailureRecorded, UpdateTask-disables,
RefreshTasks, typed observer callbacks fire, cron task
executes through runner, two runners on one pool
(idempotent DeclareTable).
### Examples
- `examples/interval_task.pas` — 5-second interval task.
- `examples/cron_task.pas` — daily 3 AM cron task.
### Build and test
```
bash build.sh # compile every src/*.pas
bash run_tests.sh # build + tests + examples
```
`fpc.cfg` provides multi-target FPC config.
### Dependencies
- fpc-log v0.1.0 — `log.types.TLogProc`
- fpc-db v0.1.0 — `database.{dialect,schema,pool}`