Jobs
Jobs are units of background work. Every job is a C++ class that derives from
Garvan::Job. They can be triggered directly, from an event listener,
or over HTTP through the admin API (which is what kalpasan job:dispatch
uses internally).
Job API
Override four things and provide one static factory:
// app/jobs/MyJob.h
#include "queue/Job.h"
#include "tools/JsonValue.h"
namespace AppJobs {
class MyJob : public Garvan::Job
{
public:
MyJob() = default;
MyJob(std::string arg) : arg_(std::move(arg)) {}
void handle() override; // do the work
std::string jobName() const override { return "MyJob"; }
std::string connection() const override { return "sync"; } // sync | async | database
Garvan::JsonValue payload() const override; // serialize for persisted queues
static std::unique_ptr<Garvan::Job>
fromPayload(const Garvan::JsonValue& p); // reverse of payload()
private:
std::string arg_;
};
} // namespace AppJobs
Registering a job
Register every user job in JobServiceProvider::register_(). Use
JobRegistry::bind() directly with the short name from
jobName():
// app/providers/JobServiceProvider.cpp
Garvan::JobRegistry::bind("MyJob", &AppJobs::MyJob::fromPayload);
GARVAN_REGISTER_JOB for user jobsThe macro GARVAN_REGISTER_JOB(AppJobs::MyJob) stringifies the
whole token and registers the key as "AppJobs::MyJob", which
then does not match jobName() or the short name
kalpasan job:dispatch expects. See the fix in
app/providers/JobServiceProvider.cpp:18.
Dispatching
Anywhere in your code — controller, listener, or another job:
#include "queue/JobDispatcher.h"
#include "app/jobs/MyJob.h"
Garvan::JobDispatcher::dispatch(std::make_unique<AppJobs::MyJob>("hello"));
The dispatcher routes the job to the driver named by
job->connection(). Drivers are bound by service providers; today
only "sync" is wired in the starter.
SyncDriver
SyncDriver runs the job inline in the calling thread. This is the
default in Phase A and gives you the same call-and-return semantics as a plain
function call — perfect for testing, low-throughput tasks, or pipelines
triggered by an HTTP request. Async and database-backed drivers will slot into
the same JobDispatcher API in later phases with no changes to your
job classes.
Example: SendTestMail
The bundled AppJobs::SendTestMail
(app/jobs/SendTestMail.h:21) is a real, sendable job. Its
handle() opens an SMTP session with libcurl, sends an HTML email
using the MAIL_* block from .env, and logs the
outcome to stdout. See Mail (SMTP) for the SMTP
side and Kalpasan CLI for the
job:dispatch verb.