# The Plantoo worker

One Node process beside the queue worker, doing the two things PHP should not (FP-T26, C2a).

**Why it exists.** Filling an official PDF means a document toolchain, and running a spreadsheet's
formulas means a formula engine. Both are Node, both are slow, and neither belongs inside a web
request. So they happen here.

**One service, two job types.** Rendering a document is built; running a workbook is D2's and
arrives as a new handler in `jobs/`, not as a second process. A supervised process costs the same
to run, deploy and watch whether it does one job or two — which is the whole reason the operations
cost is paid once.

**The contract is the `render_job` table**, not Laravel's queue. A serialised PHP job is not
readable from Node, so the two runtimes meet in plain columns: `kind` says which handler, `payload`
says what to do, `result` says what was done. Rows are claimed under a row lock, so two workers —
or a worker racing a restart — cannot render the same document twice.

## Running it

    node worker/index.js

It reads the same `.env` as the application: `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`,
`DB_PASSWORD`, and `FILESYSTEM_ROOT` (defaults to `storage/app/public`). Nothing else.

## Under supervision

Beside the queue worker, with the same policy. For supervisor:

    [program:plantoo-worker]
    command=node /var/www/plantoo/worker/index.js
    directory=/var/www/plantoo
    autostart=true
    autorestart=true
    stopwaitsecs=30
    user=www-data
    redirect_stderr=true
    stdout_logfile=/var/log/plantoo/worker.log

**`stopwaitsecs` matters.** The loop finishes the job in hand before exiting, so a deploy that kills
it mid-render leaves a row Running and a half-written file. Thirty seconds is longer than any render.

**A row stuck in `Running` is how a crashed worker is found.** Nothing reclaims one automatically,
deliberately: a document rendered twice is worse than one rendered late, and an official form filed
twice is worse again.

## Logs

One line per job, to stdout, for the supervisor to capture — `claimed`, `done` with the hash, or
`failed` with the reason. A failed job is NOT retried: a render that failed will fail the same way
next time, because the template is what it is. It waits for a person.
