@@ -3,10 +3,34 @@ namespace NeoReports.Abstractions;
33/// <summary>Lifecycle status of a report job.</summary>
44public enum ReportJobStatus
55{
6- Queued , Running , Completed , Failed , Cancelled , Paused , Retrying
6+ /// <summary>Accepted and waiting for a worker.</summary>
7+ Queued ,
8+
9+ /// <summary>Currently being processed.</summary>
10+ Running ,
11+
12+ /// <summary>Finished successfully (possibly partial when batches were skipped).</summary>
13+ Completed ,
14+
15+ /// <summary>Aborted by an unrecoverable error.</summary>
16+ Failed ,
17+
18+ /// <summary>Stopped on request before completion.</summary>
19+ Cancelled ,
20+
21+ /// <summary>Temporarily suspended (reserved for post-MVP).</summary>
22+ Paused ,
23+
24+ /// <summary>Waiting to retry after a transient failure.</summary>
25+ Retrying
726}
827
928/// <summary>Aggregate counters for a running/finished job.</summary>
29+ /// <param name="RecordsRead">Total records read from the source.</param>
30+ /// <param name="RecordsWritten">Total records written to outputs.</param>
31+ /// <param name="BytesWritten">Total bytes written across outputs.</param>
32+ /// <param name="Retries">Number of batch retries performed.</param>
33+ /// <param name="BatchesProcessed">Number of batches processed.</param>
1034public sealed record JobStats (
1135 long RecordsRead = 0 ,
1236 long RecordsWritten = 0 ,
@@ -17,6 +41,12 @@ public sealed record JobStats(
1741/// <summary>A request to enqueue a job for a registered report.</summary>
1842public sealed class ReportJobRequest
1943{
44+ /// <summary>Creates a job request for a registered report.</summary>
45+ /// <param name="reportName">Name of the registered report to run.</param>
46+ /// <param name="parameters">Run-time parameters; <c>null</c> is treated as empty.</param>
47+ /// <param name="priority">Relative weight used to route the job.</param>
48+ /// <param name="idempotencyKey">Optional key used to de-duplicate enqueue requests.</param>
49+ /// <param name="requestedBy">Optional identity of the requester.</param>
2050 public ReportJobRequest (
2151 string reportName ,
2252 IReadOnlyDictionary < string , object ? > ? parameters = null ,
@@ -31,45 +61,91 @@ public ReportJobRequest(
3161 RequestedBy = requestedBy ;
3262 }
3363
64+ /// <summary>Name of the registered report to run.</summary>
3465 public string ReportName { get ; }
66+
67+ /// <summary>Run-time parameters for the execution.</summary>
3568 public IReadOnlyDictionary < string , object ? > Parameters { get ; }
69+
70+ /// <summary>Relative weight used to route the job.</summary>
3671 public JobPriority Priority { get ; }
72+
73+ /// <summary>Optional key used to de-duplicate enqueue requests.</summary>
3774 public string ? IdempotencyKey { get ; }
75+
76+ /// <summary>Optional identity of the requester.</summary>
3877 public string ? RequestedBy { get ; }
3978}
4079
4180/// <summary>Persisted state of a report job.</summary>
4281public sealed class ReportJob
4382{
83+ /// <summary>Unique job identifier.</summary>
4484 public required string Id { get ; init ; }
85+
86+ /// <summary>Name of the report this job runs.</summary>
4587 public required string ReportName { get ; init ; }
88+
89+ /// <summary>Current lifecycle status.</summary>
4690 public required ReportJobStatus Status { get ; init ; }
91+
92+ /// <summary>UTC timestamp when the job was created.</summary>
4793 public required DateTimeOffset CreatedAt { get ; init ; }
94+
95+ /// <summary>UTC timestamp when processing started, if it has.</summary>
4896 public DateTimeOffset ? StartedAt { get ; init ; }
97+
98+ /// <summary>UTC timestamp when the job finished, if it has.</summary>
4999 public DateTimeOffset ? CompletedAt { get ; init ; }
100+
101+ /// <summary>Failure reason when <see cref="Status"/> is <see cref="ReportJobStatus.Failed"/>.</summary>
50102 public string ? Error { get ; init ; }
103+
104+ /// <summary>Aggregate counters for the job.</summary>
51105 public JobStats Stats { get ; init ; } = new ( ) ;
106+
107+ /// <summary>Optional identity of the requester.</summary>
52108 public string ? RequestedBy { get ; init ; }
109+
110+ /// <summary>Identifier of the worker that processed (or is processing) the job.</summary>
53111 public string ? WorkerId { get ; init ; }
54112}
55113
56114/// <summary>Query filter for listing jobs.</summary>
57115public sealed record JobQuery
58116{
117+ /// <summary>Restrict to a single status, or <c>null</c> for any.</summary>
59118 public ReportJobStatus ? Status { get ; init ; }
119+
120+ /// <summary>Restrict to a single report name, or <c>null</c> for any.</summary>
60121 public string ? ReportName { get ; init ; }
122+
123+ /// <summary>Only return jobs created at or after this instant.</summary>
61124 public DateTimeOffset ? Since { get ; init ; }
125+
126+ /// <summary>Maximum number of jobs to return.</summary>
62127 public int Limit { get ; init ; } = 50 ;
128+
129+ /// <summary>Number of jobs to skip (paging offset).</summary>
63130 public int Offset { get ; init ; }
64131}
65132
66133/// <summary>Persistence of job state. v1 ships InMemory and a SQL-backed (Hangfire) store.</summary>
67134public interface IJobStore
68135{
136+ /// <summary>Creates and persists a new job from a request.</summary>
69137 Task < ReportJob > CreateAsync ( ReportJobRequest request , CancellationToken cancellationToken ) ;
138+
139+ /// <summary>Loads a job by id, or returns <c>null</c> when absent.</summary>
70140 Task < ReportJob ? > GetAsync ( string jobId , CancellationToken cancellationToken ) ;
141+
142+ /// <summary>Updates the status (and optional error) of a job.</summary>
71143 Task UpdateStatusAsync ( string jobId , ReportJobStatus status , string ? error , CancellationToken cancellationToken ) ;
144+
145+ /// <summary>Updates the aggregate counters of a job.</summary>
72146 Task UpdateStatsAsync ( string jobId , JobStats stats , CancellationToken cancellationToken ) ;
147+
148+ /// <summary>Lists jobs matching a query.</summary>
73149 Task < IReadOnlyList < ReportJob > > ListAsync ( JobQuery query , CancellationToken cancellationToken ) ;
74150}
75151
@@ -79,27 +155,44 @@ public interface IJobStore
79155/// </summary>
80156public sealed class Checkpoint
81157{
158+ /// <summary>Identifier of the job this checkpoint belongs to.</summary>
82159 public required string JobId { get ; init ; }
160+
161+ /// <summary>Index of the last page that completed successfully.</summary>
83162 public int LastCompletedPage { get ; init ; }
84163
85164 /// <summary>Opaque serializable cursor (see BatchResult{T}.NextCursor).</summary>
86165 public string ? LastCursor { get ; init ; }
166+
167+ /// <summary>Number of records processed up to this checkpoint.</summary>
87168 public long RecordsProcessed { get ; init ; }
169+
170+ /// <summary>UTC timestamp when this checkpoint was written.</summary>
88171 public DateTimeOffset UpdatedAt { get ; init ; }
89172}
90173
91174/// <summary>Persistence of checkpoints. No-op implementation in v1.</summary>
92175public interface ICheckpointStore
93176{
177+ /// <summary>Persists a checkpoint.</summary>
94178 Task SaveAsync ( Checkpoint checkpoint , CancellationToken cancellationToken ) ;
179+
180+ /// <summary>Loads the latest checkpoint for a job, or <c>null</c> when absent.</summary>
95181 Task < Checkpoint ? > LoadAsync ( string jobId , CancellationToken cancellationToken ) ;
182+
183+ /// <summary>Deletes any checkpoint for a job.</summary>
96184 Task DeleteAsync ( string jobId , CancellationToken cancellationToken ) ;
97185}
98186
99187/// <summary>Enqueues and tracks report jobs. v1: single vertical worker (Hangfire single-server).</summary>
100188public interface IReportJobScheduler
101189{
190+ /// <summary>Enqueues a job and returns its id.</summary>
102191 Task < string > EnqueueAsync ( ReportJobRequest request , CancellationToken cancellationToken ) ;
192+
193+ /// <summary>Loads a job by id, or returns <c>null</c> when absent.</summary>
103194 Task < ReportJob ? > GetAsync ( string jobId , CancellationToken cancellationToken ) ;
195+
196+ /// <summary>Requests cooperative cancellation of a job; returns whether it was accepted.</summary>
104197 Task < bool > CancelAsync ( string jobId , CancellationToken cancellationToken ) ;
105198}
0 commit comments