4.3 KiB
4.3 KiB
KbsV3 Job System – How It Works
Job Lifecycle (end-to-end)
- Job created – via UI (
/adsyncjob), REST controller, or Kbs3SchedulerService- Sets
JobDto.JobType,InputData(JSON string),JobSubtype,CreatedBy - Persisted to DB via
IJobDataService.AddJob()
- Sets
- Job picked up – background poller in Kbs3ServerService finds pending jobs
- JobStartService.StartJob(job) – resolves
JobRequirementFactoryService, checksCanStart() - gRPC call – server calls microservice via
IJobSubscribeService.Subscribe(JobDataRequest)JobDataRequest.InputData= the JSON string from step 1
- Worker execution – microservice's
ILongRunningJobService.Run(request, ct)orIKbsJobhost - Streaming – protocol updates stream back via gRPC
IAsyncEnumerable<JobProtocolUpdate> - Persistence – server saves
Protocol/ProtocolLineentities
Creating a Job Programmatically
From UI page (direct DB)
job.JobType = JobType.DeleteInactiveUsers;
job.InputData = "{\"DryRun\":true}"; // raw JSON string
job.JobSubtype = JobSubtypes.SyncZadGroup;
await JobService.AddJob(job); // IJobDataService
From microservice / scheduler (via gRPC IApiComposer)
await apiComposer.CreateKnownJob(new CreateJobRequest
{
Jobtype = JobType.DeleteInactiveUsers.GetStringValue(), // "DeleteInactiveUsers"
PayLoad = "{\"DryRun\":false}", // string → JobDto.InputData as-is
Priority = 5
});
PayLoad (string) is stored directly as JobDto.InputData in CreateAndEnqueueJob (no extra serialization).
No IKnownJob definition required for this approach.
Multi-Service Jobs (e.g. DeleteInactiveUsers)
- Multiple microservices (AD, Azure, DW, Helpdesk, SQL) each register a worker for the same
JobType - All fire automatically when one job with that
JobTypeis created – server broadcasts to all registered services - No special routing needed; just one
CreateKnownJobcall triggers all
ILongRunningJobService Workers
- Dedicated
JobTypeenum entry (e.g.JobType.DeleteInactiveUsers) - Implement
Run(JobDataRequest request, CancellationToken ct) - Read input:
JsonConvert.DeserializeObject<T>(request.InputData) - Auto-registered as keyed service by
JobTypestring value inIntegrationServiceBase.AutoInjectServices() - Log via
protocolLogger.LogHeadline / LogInfo / LogWarn / LogError
IKbsJob Workers (Kbs3SqlService pattern)
- All share
JobType.CallServiceHost - Auto-registered by class name (e.g.
"SyncOpexPurchaseOrdersService") IKnownJob.ServiceName="modulename.ClassName"→ host resolves last segment to find worker- Do NOT add new
JobTypeenum entries for these
Kbs3SchedulerService
Adding a new scheduled job
- Create
MyJob : BaseJobinKbs3SchedulerService/Jobs/- Constructor:
(ILogger<MyJob> logger, IApiComposer apiComposer) : base(logger) - Implement
override string Nameandoverride async Task Execute(CancellationToken, KeyValue[] settings) - Auto-registered via
AddAllTypesOf<IJob>()– no manual DI
- Constructor:
- Add entry to
appsettings.Development.json(IsEnabled: false) andappsettings.Production.json(IsEnabled: true) underSchedulerSettings.Items
Schedule patterns
| Type | Meaning | Key fields |
|---|---|---|
| 1 | Daily | Interval, StartTime |
| 2 | Weekly | Interval, DaysOfWeek: ["Monday","Friday",...] |
| 3 | Monthly | DaysOfMonth |
| 5 | Interval | CustomInterval: "HH:mm:ss" |
StartTimeon the root item = time of day for executionSettingsarray → read inExecuteviasettings.FirstOrDefault(s => s.Key == "Foo")?.Value
Internal flow
SchedulerHostedService → SeriesSchedulerManager → SeriesJobScheduler (infinite loop)
→ _schedule.GetNextExecution(DateTime.Now) → Task.Delay(...) → job.ExecuteAsync(...)
InputData / Payload flow
CreateJobRequest.PayLoad (string)
→ ApiComposerService.CreateKnownJob()
→ CreateJobService.CreateAndEnqueueJob()
→ if (payload is string) input = payload as string ← no extra serialization!
→ JobDto.InputData
→ JobDataRequest.InputData (sent to microservice via gRPC)
→ worker reads request.InputData