@lde/pipeline 0.48.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pipeline.d.ts +7 -15
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +37 -22
- package/dist/sparql/reader.d.ts +4 -4
- package/dist/sparql/reader.d.ts.map +1 -1
- package/dist/sparql/reader.js +1 -3
- package/dist/stage.d.ts +30 -2
- package/dist/stage.d.ts.map +1 -1
- package/dist/stage.js +20 -6
- package/package.json +1 -1
package/dist/pipeline.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Dataset } from '@lde/dataset';
|
|
2
|
-
import type { HostLimiter } from '@lde/host-limiter';
|
|
3
2
|
import type { Quad } from '@rdfjs/types';
|
|
4
3
|
import type { DatasetSelector } from './selector.js';
|
|
5
4
|
import { Stage } from './stage.js';
|
|
@@ -120,18 +119,6 @@ export interface PipelineOptions<Out = Quad> {
|
|
|
120
119
|
* once per dataset so state resets between datasets.
|
|
121
120
|
*/
|
|
122
121
|
timeout?: () => TimeoutPolicy;
|
|
123
|
-
/**
|
|
124
|
-
* The limiter every stage’s readers run their queries on, keyed on the
|
|
125
|
-
* endpoint ({@link endpointKeyOf}). Each endpoint’s limit adapts to how it
|
|
126
|
-
* responds, up to the limiter’s per-host ceiling, and carries over to the
|
|
127
|
-
* next dataset it serves; see {@link SparqlConstructReader}.
|
|
128
|
-
*
|
|
129
|
-
* Pass the limiter you also give a resolver that loads the same backend –
|
|
130
|
-
* keyed on the endpoint too – so the two share one limit.
|
|
131
|
-
*
|
|
132
|
-
* Left out, the readers share a default: `createHostLimiter(10, 10)`.
|
|
133
|
-
*/
|
|
134
|
-
limiter?: HostLimiter;
|
|
135
122
|
}
|
|
136
123
|
export declare class Pipeline<Out = Quad> {
|
|
137
124
|
private readonly name;
|
|
@@ -144,7 +131,13 @@ export declare class Pipeline<Out = Quad> {
|
|
|
144
131
|
private readonly chaining?;
|
|
145
132
|
private readonly reporters;
|
|
146
133
|
private readonly timeoutFactory;
|
|
147
|
-
|
|
134
|
+
/**
|
|
135
|
+
* The limiter every stage’s readers run their queries on, keyed on the
|
|
136
|
+
* endpoint, so each endpoint’s limit adapts to how it responds and carries
|
|
137
|
+
* over to the next dataset it serves; see {@link SparqlConstructReader}.
|
|
138
|
+
* Its ceiling is the highest `maxConcurrency` of any stage.
|
|
139
|
+
*/
|
|
140
|
+
private readonly limiter;
|
|
148
141
|
private readonly provenanceStore?;
|
|
149
142
|
private readonly pipelineVersion?;
|
|
150
143
|
constructor(options: PipelineOptions<Out>);
|
|
@@ -155,7 +148,6 @@ export declare class Pipeline<Out = Quad> {
|
|
|
155
148
|
/** Persist the processing record for a dataset, when a store is configured. */
|
|
156
149
|
private recordOutcome;
|
|
157
150
|
private reportValidators;
|
|
158
|
-
private collectStages;
|
|
159
151
|
/**
|
|
160
152
|
* The writer a stage's merged output is written through: the open run
|
|
161
153
|
* wrapped with the plugins' {@link PipelinePlugin.beforeStageWrite}
|
package/dist/pipeline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAgB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
|
package/dist/pipeline.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createReadStream } from 'node:fs';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { createHostLimiter, positiveIntOrDefault, } from '@lde/host-limiter';
|
|
3
4
|
import { StreamParser } from 'n3';
|
|
4
5
|
import { AsyncQueue } from './asyncQueue.js';
|
|
5
6
|
import { FileWriter } from './writer/fileWriter.js';
|
|
@@ -240,6 +241,12 @@ export class Pipeline {
|
|
|
240
241
|
chaining;
|
|
241
242
|
reporters;
|
|
242
243
|
timeoutFactory;
|
|
244
|
+
/**
|
|
245
|
+
* The limiter every stage’s readers run their queries on, keyed on the
|
|
246
|
+
* endpoint, so each endpoint’s limit adapts to how it responds and carries
|
|
247
|
+
* over to the next dataset it serves; see {@link SparqlConstructReader}.
|
|
248
|
+
* Its ceiling is the highest `maxConcurrency` of any stage.
|
|
249
|
+
*/
|
|
243
250
|
limiter;
|
|
244
251
|
provenanceStore;
|
|
245
252
|
pipelineVersion;
|
|
@@ -282,7 +289,8 @@ export class Pipeline {
|
|
|
282
289
|
this.reporters = [options.reporter ?? []].flat();
|
|
283
290
|
this.timeoutFactory =
|
|
284
291
|
options.timeout ?? (() => new ConstantTimeoutPolicy(300_000));
|
|
285
|
-
|
|
292
|
+
const ceiling = positiveIntOrDefault(Math.max(...[...allStages(options.stages)].map((stage) => stage.maxConcurrency)), 10);
|
|
293
|
+
this.limiter = createHostLimiter(ceiling, ceiling);
|
|
286
294
|
this.provenanceStore = options.provenanceStore;
|
|
287
295
|
this.pipelineVersion = options.pipelineVersion;
|
|
288
296
|
}
|
|
@@ -491,7 +499,7 @@ export class Pipeline {
|
|
|
491
499
|
// completes.
|
|
492
500
|
let unread;
|
|
493
501
|
try {
|
|
494
|
-
({ stageFailed, unread } = await this.runStages(dataset, resolved.distribution, timeout, runWriter, context));
|
|
502
|
+
({ stageFailed, unread } = await this.runStages(dataset, resolved.distribution, sourceKindOf(resolved), timeout, runWriter, context));
|
|
495
503
|
// Reactive fallback: an endpoint that passed probing but could not serve
|
|
496
504
|
// the analysis stages is empirically incapable. Switch to the dataset’s
|
|
497
505
|
// data dump and re-run all stages locally, discarding the
|
|
@@ -524,7 +532,7 @@ export class Pipeline {
|
|
|
524
532
|
// Discard the endpoint-sourced partial output before the re-run so
|
|
525
533
|
// the dump-sourced stats replace it rather than appending to it.
|
|
526
534
|
await runWriter.reset?.(dataset);
|
|
527
|
-
({ stageFailed, unread } = await this.runStages(dataset, fallback.distribution, timeout, runWriter, context));
|
|
535
|
+
({ stageFailed, unread } = await this.runStages(dataset, fallback.distribution, sourceKindOf(fallback), timeout, runWriter, context));
|
|
528
536
|
// Adopt the dump’s fingerprint only once the re-run has succeeded.
|
|
529
537
|
// If the reset or the re-run throws, or a stage fails against the
|
|
530
538
|
// dump, the dataset is recorded ‘failed’ under the endpoint’s
|
|
@@ -637,7 +645,7 @@ export class Pipeline {
|
|
|
637
645
|
}
|
|
638
646
|
async reportValidators(runId, dataset) {
|
|
639
647
|
const validators = new Set();
|
|
640
|
-
for (const stage of
|
|
648
|
+
for (const stage of allStages(this.stages)) {
|
|
641
649
|
if (stage.validator)
|
|
642
650
|
validators.add(stage.validator);
|
|
643
651
|
}
|
|
@@ -648,13 +656,6 @@ export class Pipeline {
|
|
|
648
656
|
}
|
|
649
657
|
// Walks every stage and sub-stage to collect validators; it reads only
|
|
650
658
|
// `validator`/`stages`, never output, so it is agnostic to the item type.
|
|
651
|
-
*collectStages(stages) {
|
|
652
|
-
for (const stage of stages) {
|
|
653
|
-
yield stage;
|
|
654
|
-
if (stage.stages.length > 0)
|
|
655
|
-
yield* this.collectStages(stage.stages);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
659
|
/**
|
|
659
660
|
* The writer a stage's merged output is written through: the open run
|
|
660
661
|
* wrapped with the plugins' {@link PipelinePlugin.beforeStageWrite}
|
|
@@ -706,16 +707,16 @@ export class Pipeline {
|
|
|
706
707
|
* reacts to – and what the stages could not read, which belongs to this
|
|
707
708
|
* attempt alone.
|
|
708
709
|
*/
|
|
709
|
-
async runStages(dataset, distribution, timeout, runWriter, context) {
|
|
710
|
+
async runStages(dataset, distribution, source, timeout, runWriter, context) {
|
|
710
711
|
const unread = new UnreadValueTally();
|
|
711
712
|
let stageFailed = false;
|
|
712
713
|
for (const stage of this.stages) {
|
|
713
714
|
try {
|
|
714
715
|
if (stage.stages.length > 0) {
|
|
715
|
-
await this.runChain(dataset, distribution, stage, runWriter, context, timeout);
|
|
716
|
+
await this.runChain(dataset, distribution, source, stage, runWriter, context, timeout);
|
|
716
717
|
}
|
|
717
718
|
else {
|
|
718
|
-
await this.runStage(context.runId, dataset, distribution, stage, this.stageWriter(runWriter, stage.name), timeout, unread);
|
|
719
|
+
await this.runStage(context.runId, dataset, distribution, stage, this.stageWriter(runWriter, stage.name), source, timeout, unread);
|
|
719
720
|
}
|
|
720
721
|
}
|
|
721
722
|
catch (error) {
|
|
@@ -729,13 +730,14 @@ export class Pipeline {
|
|
|
729
730
|
* Run a stage with reporting and return whether it was supported.
|
|
730
731
|
* Returns `true` if the stage produced results, `false` if NotSupported.
|
|
731
732
|
*/
|
|
732
|
-
async runStage(runId, dataset, distribution, stage, writer, timeout, unread) {
|
|
733
|
+
async runStage(runId, dataset, distribution, stage, writer, source, timeout, unread) {
|
|
733
734
|
this.report(runId, { type: 'stageStart', dataset, stage: stage.name });
|
|
734
735
|
const stageStart = Date.now();
|
|
735
736
|
let itemsProcessed = 0;
|
|
736
737
|
let quadsGenerated = 0;
|
|
737
738
|
let summary;
|
|
738
739
|
const result = await stage.run(dataset, distribution, writer, {
|
|
740
|
+
source,
|
|
739
741
|
onProgress: (items, quads) => {
|
|
740
742
|
itemsProcessed = items;
|
|
741
743
|
quadsGenerated = quads;
|
|
@@ -790,13 +792,13 @@ export class Pipeline {
|
|
|
790
792
|
return true;
|
|
791
793
|
}
|
|
792
794
|
/** Run a stage in chained mode, throwing if the stage is not supported. */
|
|
793
|
-
async runChainedStage(runId, dataset, distribution, stage, writer, timeout) {
|
|
794
|
-
const supported = await this.runStage(runId, dataset, distribution, stage, writer, timeout);
|
|
795
|
+
async runChainedStage(runId, dataset, distribution, stage, writer, source, timeout) {
|
|
796
|
+
const supported = await this.runStage(runId, dataset, distribution, stage, writer, source, timeout);
|
|
795
797
|
if (!supported) {
|
|
796
798
|
throw new Error(`Stage '${stage.name}' returned NotSupported in chained mode`);
|
|
797
799
|
}
|
|
798
800
|
}
|
|
799
|
-
async runChain(dataset, distribution, stage, runWriter, context, timeout) {
|
|
801
|
+
async runChain(dataset, distribution, source, stage, runWriter, context, timeout) {
|
|
800
802
|
const { stageOutputResolver, outputDir } = this.chaining;
|
|
801
803
|
const outputFiles = [];
|
|
802
804
|
// Chaining is a quad-only concern: every stage in a chain writes N-Triples
|
|
@@ -805,14 +807,14 @@ export class Pipeline {
|
|
|
805
807
|
// requirement "sub-stages imply `Out` is `Quad`" is not expressible – `stages`
|
|
806
808
|
// and `chaining` are independent options – so it is a runtime contract, and
|
|
807
809
|
// the scratch machinery treats the parent stage as `Stage<Quad>`.
|
|
808
|
-
const runScratchStage = async (chainedStage, stageDistribution) => {
|
|
810
|
+
const runScratchStage = async (chainedStage, stageDistribution, stageSource) => {
|
|
809
811
|
const scratchWriter = new FileWriter({
|
|
810
812
|
outputDir: `${outputDir}/${chainedStage.name}`,
|
|
811
813
|
format: 'n-triples',
|
|
812
814
|
});
|
|
813
815
|
const scratchRun = await scratchWriter.openRun(context);
|
|
814
816
|
try {
|
|
815
|
-
await this.runChainedStage(context.runId, dataset, stageDistribution, chainedStage, scratchRun, timeout);
|
|
817
|
+
await this.runChainedStage(context.runId, dataset, stageDistribution, chainedStage, scratchRun, stageSource, timeout);
|
|
816
818
|
await scratchRun.flush(dataset, 'success');
|
|
817
819
|
await scratchRun.commit();
|
|
818
820
|
}
|
|
@@ -824,12 +826,14 @@ export class Pipeline {
|
|
|
824
826
|
};
|
|
825
827
|
try {
|
|
826
828
|
// 1. Run parent stage → scratch FileWriter (quad-only, per above).
|
|
827
|
-
const parentOutput = await runScratchStage(stage, distribution);
|
|
829
|
+
const parentOutput = await runScratchStage(stage, distribution, source);
|
|
828
830
|
outputFiles.push(parentOutput);
|
|
829
831
|
// 2. Chain through children.
|
|
830
832
|
let currentDistribution = await stageOutputResolver.resolve(parentOutput);
|
|
831
833
|
for (let i = 0; i < stage.stages.length; i++) {
|
|
832
|
-
|
|
834
|
+
// A child reads its parent’s output, which the pipeline serves from
|
|
835
|
+
// a store of its own, as it does an imported dump.
|
|
836
|
+
const childOutput = await runScratchStage(stage.stages[i], currentDistribution, 'imported');
|
|
833
837
|
outputFiles.push(childOutput);
|
|
834
838
|
if (i < stage.stages.length - 1) {
|
|
835
839
|
currentDistribution = await stageOutputResolver.resolve(childOutput);
|
|
@@ -861,6 +865,13 @@ export class Pipeline {
|
|
|
861
865
|
}
|
|
862
866
|
}
|
|
863
867
|
}
|
|
868
|
+
/** Every stage, with the stages chained under it. */
|
|
869
|
+
function* allStages(stages) {
|
|
870
|
+
for (const stage of stages) {
|
|
871
|
+
yield stage;
|
|
872
|
+
yield* allStages(stage.stages);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
864
875
|
function mapProbeResult(distribution, result) {
|
|
865
876
|
const fingerprint = sourceFingerprint(distribution, result);
|
|
866
877
|
if (result instanceof NetworkError) {
|
|
@@ -883,3 +894,7 @@ function mapProbeResult(distribution, result) {
|
|
|
883
894
|
fingerprint,
|
|
884
895
|
};
|
|
885
896
|
}
|
|
897
|
+
/** What kind of source a resolved distribution serves the stages. */
|
|
898
|
+
function sourceKindOf(resolved) {
|
|
899
|
+
return resolved.imported === undefined ? 'endpoint' : 'imported';
|
|
900
|
+
}
|
package/dist/sparql/reader.d.ts
CHANGED
|
@@ -40,12 +40,12 @@ export interface ReadOptions {
|
|
|
40
40
|
timeout?: TimeoutPolicy;
|
|
41
41
|
/**
|
|
42
42
|
* The limiter every query of a read goes through, keyed on the endpoint
|
|
43
|
-
* ({@link endpointKeyOf}), so every
|
|
44
|
-
*
|
|
45
|
-
* backend – shares one adaptive limit and one pause. See
|
|
43
|
+
* ({@link endpointKeyOf}), so every read on one endpoint shares one adaptive
|
|
44
|
+
* limit and one pause. A pipeline passes its own. See
|
|
46
45
|
* {@link SparqlConstructReader}.
|
|
47
46
|
*
|
|
48
|
-
*
|
|
47
|
+
* A read not given one gets a limiter for that read alone:
|
|
48
|
+
* `createHostLimiter(10, 10)`. A {@link Stage} passes one for all its batches.
|
|
49
49
|
*/
|
|
50
50
|
limiter?: HostLimiter;
|
|
51
51
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/sparql/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAiB,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAGpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAKxC,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/sparql/reader.ts"],"names":[],"mappings":"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"}
|
package/dist/sparql/reader.js
CHANGED
|
@@ -18,8 +18,6 @@ import { classifyOutcome, isTransientError, statusOf } from './failure.js';
|
|
|
18
18
|
* matters when the reader is driven directly (without a Pipeline).
|
|
19
19
|
*/
|
|
20
20
|
const defaultTimeoutPolicy = new ConstantTimeoutPolicy(300_000);
|
|
21
|
-
/** The limiter of every read not given one, so they share one limit. */
|
|
22
|
-
const defaultLimiter = createHostLimiter(10, 10);
|
|
23
21
|
/**
|
|
24
22
|
* A reader could not run because the dataset lacks a supported distribution.
|
|
25
23
|
*/
|
|
@@ -129,7 +127,7 @@ export class SparqlConstructReader {
|
|
|
129
127
|
const quads = await this.readBatch(bindings, query, {
|
|
130
128
|
endpoint,
|
|
131
129
|
key: endpointKeyOf(endpoint),
|
|
132
|
-
limiter: options?.limiter ??
|
|
130
|
+
limiter: options?.limiter ?? createHostLimiter(10, 10),
|
|
133
131
|
policy: options?.timeout ?? defaultTimeoutPolicy,
|
|
134
132
|
onRootSkipped: (root, error) => {
|
|
135
133
|
skipped++;
|
package/dist/stage.d.ts
CHANGED
|
@@ -105,9 +105,19 @@ export interface StageOptions<Out = Quad> {
|
|
|
105
105
|
* fills exactly one batch. A `LIMIT` clause in the selector query
|
|
106
106
|
* overrides this for endpoints with hard result limits.
|
|
107
107
|
*
|
|
108
|
+
* Give a value per {@link SourceKind} where the two kinds of source want
|
|
109
|
+
* different sizes: an engine that imported a dump for this run alone
|
|
110
|
+
* answers a large batch easily, while a live endpoint shared with others
|
|
111
|
+
* may fail on one. The pipeline tells the stage which kind it reads
|
|
112
|
+
* ({@link RunOptions.source}).
|
|
113
|
+
*
|
|
114
|
+
* ```ts
|
|
115
|
+
* batchSize: { endpoint: 10, imported: 100 }
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
108
118
|
* @default 10
|
|
109
119
|
*/
|
|
110
|
-
batchSize?: number
|
|
120
|
+
batchSize?: number | Readonly<Record<SourceKind, number>>;
|
|
111
121
|
/**
|
|
112
122
|
* Maximum concurrent in-flight SPARQL queries. Within each batch, all
|
|
113
123
|
* readers run in parallel; the number of concurrent batches is
|
|
@@ -172,8 +182,23 @@ export interface StageOptions<Out = Quad> {
|
|
|
172
182
|
onInvalid?: 'write' | 'skip' | 'halt';
|
|
173
183
|
};
|
|
174
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* What a stage reads: a live SPARQL `endpoint` the dataset publishes, or a
|
|
187
|
+
* data dump the pipeline `imported` into a store of its own for the run.
|
|
188
|
+
*/
|
|
189
|
+
export type SourceKind = 'endpoint' | 'imported';
|
|
175
190
|
export interface RunOptions extends Pick<ReadOptions, 'limiter' | 'onRootSkipped'> {
|
|
176
191
|
onProgress?: (itemsProcessed: number, quadsGenerated: number) => void;
|
|
192
|
+
/**
|
|
193
|
+
* What the distribution handed to {@link Stage.run} serves, which picks the
|
|
194
|
+
* {@link StageOptions.batchSize} of a stage that gives one per kind. A stage
|
|
195
|
+
* whose {@link StageOptions.sourceFor} substitutes another distribution reads
|
|
196
|
+
* a source the pipeline did not resolve, so it reads that as an `endpoint`
|
|
197
|
+
* whatever this says.
|
|
198
|
+
*
|
|
199
|
+
* @default 'endpoint'
|
|
200
|
+
*/
|
|
201
|
+
source?: SourceKind;
|
|
177
202
|
/**
|
|
178
203
|
* Per-dataset {@link TimeoutPolicy} threaded through to readers and
|
|
179
204
|
* item selectors. The Pipeline owns lifecycle (factory invocation per
|
|
@@ -201,10 +226,13 @@ export declare class Stage<Out = Quad> {
|
|
|
201
226
|
/** Whether this stage reads a source of its own rather than the dataset’s
|
|
202
227
|
* resolved distribution. @see {@link StageOptions.sourceFor} */
|
|
203
228
|
readonly sourcesOwnData: boolean;
|
|
229
|
+
/** Maximum concurrent in-flight SPARQL queries. @see {@link StageOptions.maxConcurrency} */
|
|
230
|
+
readonly maxConcurrency: number;
|
|
204
231
|
private readonly readers;
|
|
205
232
|
private readonly itemSelector?;
|
|
206
233
|
private readonly batchSize;
|
|
207
|
-
|
|
234
|
+
/** The limiter of a run not given one, so its batches back off together. */
|
|
235
|
+
private readonly limiter;
|
|
208
236
|
private readonly validation?;
|
|
209
237
|
private readonly project?;
|
|
210
238
|
private readonly queueCapacity?;
|
package/dist/stage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../src/stage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../src/stage.ts"],"names":[],"mappings":"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"}
|
package/dist/stage.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHostLimiter, positiveIntOrDefault, } from '@lde/host-limiter';
|
|
1
2
|
import { NoItemsSelected, NotSupported } from './sparql/reader.js';
|
|
2
3
|
import { batch } from './batch.js';
|
|
3
4
|
import { AsyncQueue } from './asyncQueue.js';
|
|
@@ -10,10 +11,13 @@ export class Stage {
|
|
|
10
11
|
/** Whether this stage reads a source of its own rather than the dataset’s
|
|
11
12
|
* resolved distribution. @see {@link StageOptions.sourceFor} */
|
|
12
13
|
sourcesOwnData;
|
|
14
|
+
/** Maximum concurrent in-flight SPARQL queries. @see {@link StageOptions.maxConcurrency} */
|
|
15
|
+
maxConcurrency;
|
|
13
16
|
readers;
|
|
14
17
|
itemSelector;
|
|
15
18
|
batchSize;
|
|
16
|
-
|
|
19
|
+
/** The limiter of a run not given one, so its batches back off together. */
|
|
20
|
+
limiter;
|
|
17
21
|
validation;
|
|
18
22
|
project;
|
|
19
23
|
queueCapacity;
|
|
@@ -39,6 +43,8 @@ export class Stage {
|
|
|
39
43
|
this.itemSelector = options.itemSelector;
|
|
40
44
|
this.batchSize = options.batchSize ?? 10;
|
|
41
45
|
this.maxConcurrency = options.maxConcurrency ?? 10;
|
|
46
|
+
const ceiling = positiveIntOrDefault(this.maxConcurrency, 10);
|
|
47
|
+
this.limiter = createHostLimiter(ceiling, ceiling);
|
|
42
48
|
this.validation = options.validation;
|
|
43
49
|
this.expectsOutput = options.expectsOutput ?? false;
|
|
44
50
|
this.project = options.project;
|
|
@@ -54,7 +60,7 @@ export class Stage {
|
|
|
54
60
|
const timeout = options?.timeout;
|
|
55
61
|
const readOptions = {
|
|
56
62
|
timeout,
|
|
57
|
-
limiter: options?.limiter,
|
|
63
|
+
limiter: options?.limiter ?? this.limiter,
|
|
58
64
|
onRootSkipped: options?.onRootSkipped,
|
|
59
65
|
};
|
|
60
66
|
// Substituted once, before anything reads: the selector and the readers
|
|
@@ -64,9 +70,17 @@ export class Stage {
|
|
|
64
70
|
: resolved;
|
|
65
71
|
const tally = new StageTally(this.transformNames);
|
|
66
72
|
if (this.itemSelector) {
|
|
67
|
-
|
|
73
|
+
// A substituted source is one the pipeline did not resolve; one handed
|
|
74
|
+
// back unchanged is still the source the pipeline resolved.
|
|
75
|
+
const source = distribution === resolved
|
|
76
|
+
? (options?.source ?? 'endpoint')
|
|
77
|
+
: 'endpoint';
|
|
78
|
+
const batchSize = typeof this.batchSize === 'number'
|
|
79
|
+
? this.batchSize
|
|
80
|
+
: this.batchSize[source];
|
|
81
|
+
const result = await this.runWithSelector(tally.selected(this.itemSelector.select(distribution, batchSize, {
|
|
68
82
|
timeout,
|
|
69
|
-
})), dataset, distribution, writer, tally, readOptions, options);
|
|
83
|
+
})), batchSize, dataset, distribution, writer, tally, readOptions, options);
|
|
70
84
|
if (!(result instanceof NotSupported)) {
|
|
71
85
|
options?.onComplete?.(tally.summary());
|
|
72
86
|
}
|
|
@@ -138,10 +152,10 @@ export class Stage {
|
|
|
138
152
|
throw new Error(`Stage '${this.name}' expected output but produced none`);
|
|
139
153
|
}
|
|
140
154
|
}
|
|
141
|
-
async runWithSelector(selector, dataset, distribution, writer, tally, readOptions, options) {
|
|
155
|
+
async runWithSelector(selector, batchSize, dataset, distribution, writer, tally, readOptions, options) {
|
|
142
156
|
// Peek the first batch to detect an empty selector before starting the
|
|
143
157
|
// writer (important because e.g. SparqlUpdateWriter does CLEAR GRAPH).
|
|
144
|
-
const batches = batch(selector,
|
|
158
|
+
const batches = batch(selector, batchSize);
|
|
145
159
|
const iter = batches[Symbol.asyncIterator]();
|
|
146
160
|
const first = await iter.next();
|
|
147
161
|
if (first.done) {
|