-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
142 lines (127 loc) · 5.38 KB
/
Copy pathindex.js
File metadata and controls
142 lines (127 loc) · 5.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// @ts-check
import path from 'node:path'
import { createSinkWatermarkStore } from '../../../src/core/sinks/watermarks.js'
import { validateCentralConfig } from './src/config.js'
import { createConfigPullLoop } from './src/config_client.js'
import { IdentityClient } from './src/identity_client.js'
import { bindDestinationState, createDatasetRolloutStore, markDestinationStateReady } from './src/rollout.js'
import { createForwardSink, initializeOpenDatasetRollouts } from './src/sink.js'
/**
* @import { PluginActivationContext, SinkCreateContext } from '../../../hypaware-plugin-kernel-types.js'
*/
/**
* `@hypaware/central`: request sink that forwards ready cache
* partitions to a central HypAware server. The plugin replaces the
* `role: gateway` config from collectivus: a host becomes "the
* gateway" purely by configuring this sink under
* `HypAwareV2Config.sinks.<name>` with `plugin: "@hypaware/central"`.
*
* Activate captures `ctx.query` / `ctx.storage` in closure so each sink
* instance can read partitions during `exportBatch` (the
* `SinkCreateContext` itself does not carry storage/query).
*
* @param {PluginActivationContext} ctx
*/
export async function activate(ctx) {
const query = ctx.query
const storage = ctx.storage
// Present only in daemon mode. Without an apply engine there is no
// one to hand a pulled document to, so the pull loop stays off (CLI
// boots must not fire config polls as a side effect of `hyp status`).
const configControl = ctx.configControl
ctx.sinks.register({
name: 'forward',
plugin: '@hypaware/central',
supports: [],
/**
* @param {SinkCreateContext} sinkCtx
*/
async create(sinkCtx) {
const validation = validateCentralConfig(sinkCtx.config)
if (!validation.ok) {
throw new Error(`@hypaware/central: ${validation.message}`)
}
const config = validation.config
const persistedPath = config.identity.persisted_path
?? path.join(sinkCtx.paths.stateDir, 'identity.json')
const identityClient = new IdentityClient({
centralUrl: config.url,
bootstrapToken: config.identity.bootstrap_token,
persistedPath,
})
const source = await identityClient.acquire()
sinkCtx.log.info('central.identity.acquired', {
hyp_sink_instance: sinkCtx.name,
hyp_identity_source: source,
})
// Bind progress before creating either state store. Existing unscoped
// progress is adopted once for the current destination; a new origin/org
// gets an isolated scope durably marked for retained-history replay.
// @ref LLP 0315#destination-identity [implements]: watermarks and rollout manifests share one destination-scoped state root
let destinationState = await bindDestinationState({
paths: sinkCtx.paths,
instanceName: sinkCtx.name,
destination: identityClient.getDestination(),
})
sinkCtx.log.info('central.destination.bound', {
hyp_sink_instance: sinkCtx.name,
destination_origin: destinationState.destination.origin,
destination_org: destinationState.destination.org,
destination_phase: destinationState.phase,
adopted_legacy_progress: destinationState.adoptedLegacy,
})
const watermarks = createSinkWatermarkStore({ stateDir: destinationState.stateDir })
const rollouts = createDatasetRolloutStore({ stateDir: destinationState.stateDir })
// Establish open-dataset rollout state during sink creation. An existing
// destination's software rollout baselines current partitions; a new
// destination starts them at zero so retained eligible history forwards.
// An empty dataset still gets a durable manifest before its first row.
// @ref LLP 0307#rollout-instant [implements]: initialize dataset rollout state before scheduled exports can observe a first partition
// @ref LLP 0315#new-destination-replay [implements]: a newly bound destination initializes eligible open datasets for retained-history replay
await initializeOpenDatasetRollouts({
query,
storage,
watermarks,
rollouts,
log: sinkCtx.log,
replayRetainedHistory: destinationState.phase === 'initializing-history',
})
if (destinationState.phase === 'initializing-history') {
destinationState = await markDestinationStateReady(destinationState)
sinkCtx.log.info('central.destination.ready', {
hyp_sink_instance: sinkCtx.name,
destination_origin: destinationState.destination.origin,
destination_org: destinationState.destination.org,
})
}
const sink = createForwardSink({
config,
identityClient,
query,
storage,
watermarks,
rollouts,
log: sinkCtx.log,
})
if (!configControl) return sink
// @ref LLP 0025#config-pull-loop [implements]: pull immediately on bootstrap success, then on the steady timer
const pullLoop = createConfigPullLoop({
centralUrl: config.url,
identityClient,
configControl,
...(config.poll_interval_seconds !== undefined
? { pollIntervalSeconds: config.poll_interval_seconds }
: {}),
log: sinkCtx.log,
})
pullLoop.start()
return {
...sink,
async close() {
await pullLoop.stop()
await sink.close()
},
}
},
})
}