@interopio/bridge 0.0.1-alpha → 0.0.3-beta

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.
Files changed (84) hide show
  1. package/bin/bridge.js +1 -1
  2. package/dist/main.js +2139 -0
  3. package/dist/main.js.map +7 -0
  4. package/package.json +9 -6
  5. package/gen/instance/GeneratedBuildInfo.ts +0 -4
  6. package/src/cluster/Address.ts +0 -57
  7. package/src/cluster/Cluster.ts +0 -13
  8. package/src/cluster/Endpoint.ts +0 -5
  9. package/src/cluster/Member.ts +0 -9
  10. package/src/cluster/MembershipListener.ts +0 -6
  11. package/src/config/Config.ts +0 -100
  12. package/src/config/DiscoveryConfig.ts +0 -21
  13. package/src/config/Duration.ts +0 -168
  14. package/src/config/KubernetesConfig.ts +0 -7
  15. package/src/config/NamedDiscoveryConfig.ts +0 -17
  16. package/src/config/Properties.ts +0 -49
  17. package/src/config/index.ts +0 -1
  18. package/src/discovery/SimpleDiscoveryNode.ts +0 -14
  19. package/src/discovery/index.ts +0 -207
  20. package/src/discovery/multicast/MulticastDiscoveryStrategy.ts +0 -141
  21. package/src/discovery/multicast/MulticastDiscoveryStrategyFactory.ts +0 -30
  22. package/src/discovery/multicast/MulticastProperties.ts +0 -4
  23. package/src/discovery/settings.ts +0 -37
  24. package/src/error/RequestFailure.ts +0 -48
  25. package/src/gossip/ApplicationState.ts +0 -48
  26. package/src/gossip/EndpointState.ts +0 -141
  27. package/src/gossip/FailureDetector.ts +0 -235
  28. package/src/gossip/Gossiper.ts +0 -1133
  29. package/src/gossip/HeartbeatState.ts +0 -66
  30. package/src/gossip/Messenger.ts +0 -130
  31. package/src/gossip/VersionedValue.ts +0 -59
  32. package/src/index.ts +0 -3
  33. package/src/instance/AddressPicker.ts +0 -245
  34. package/src/instance/BridgeNode.ts +0 -141
  35. package/src/instance/ClusterTopologyIntentTracker.ts +0 -4
  36. package/src/io/VersionedSerializer.ts +0 -230
  37. package/src/io/util.ts +0 -117
  38. package/src/kubernetes/DnsEndpointResolver.ts +0 -70
  39. package/src/kubernetes/KubernetesApiEndpointResolver.ts +0 -111
  40. package/src/kubernetes/KubernetesApiProvider.ts +0 -75
  41. package/src/kubernetes/KubernetesClient.ts +0 -264
  42. package/src/kubernetes/KubernetesConfig.ts +0 -130
  43. package/src/kubernetes/KubernetesDiscoveryStrategy.ts +0 -30
  44. package/src/kubernetes/KubernetesDiscoveryStrategyFactory.ts +0 -71
  45. package/src/kubernetes/KubernetesEndpointResolver.ts +0 -43
  46. package/src/kubernetes/KubernetesProperties.ts +0 -22
  47. package/src/license/BridgeLicenseValidator.ts +0 -19
  48. package/src/license/LicenseValidator.ts +0 -114
  49. package/src/license/types.ts +0 -40
  50. package/src/logging.ts +0 -22
  51. package/src/main.mts +0 -53
  52. package/src/net/Action.ts +0 -143
  53. package/src/net/AddressSerializer.ts +0 -44
  54. package/src/net/ByteBufferAllocator.ts +0 -27
  55. package/src/net/FrameDecoder.ts +0 -314
  56. package/src/net/FrameEncoder.ts +0 -138
  57. package/src/net/HandshakeProtocol.ts +0 -143
  58. package/src/net/InboundConnection.ts +0 -108
  59. package/src/net/InboundConnectionInitiator.ts +0 -150
  60. package/src/net/InboundMessageHandler.ts +0 -377
  61. package/src/net/InboundSink.ts +0 -38
  62. package/src/net/Message.ts +0 -428
  63. package/src/net/OutboundConnection.ts +0 -1141
  64. package/src/net/OutboundConnectionInitiator.ts +0 -76
  65. package/src/net/RequestCallbacks.ts +0 -148
  66. package/src/net/ResponseHandler.ts +0 -30
  67. package/src/net/ShareableBytes.ts +0 -125
  68. package/src/net/internal/AsyncResourceExecutor.ts +0 -464
  69. package/src/net/internal/AsyncSocketPromise.ts +0 -37
  70. package/src/net/internal/channel/ChannelHandlerAdapter.ts +0 -99
  71. package/src/net/internal/channel/types.ts +0 -188
  72. package/src/utils/bigint.ts +0 -23
  73. package/src/utils/buffer.ts +0 -434
  74. package/src/utils/clock.ts +0 -148
  75. package/src/utils/collections.ts +0 -283
  76. package/src/utils/crc.ts +0 -39
  77. package/src/utils/internal/IpAddressUtil.ts +0 -161
  78. package/src/utils/memory/BufferPools.ts +0 -40
  79. package/src/utils/network.ts +0 -130
  80. package/src/utils/promise.ts +0 -38
  81. package/src/utils/uuid.ts +0 -5
  82. package/src/utils/vint.ts +0 -238
  83. package/src/version/MemberVersion.ts +0 -42
  84. package/src/version/Version.ts +0 -12
@@ -1,1133 +0,0 @@
1
- import getLogger from '../logging.ts';
2
- import {type Message, MessageFactory} from '../net/Message.ts';
3
- import type {DataInput, DataOutput, VersionedSerializer} from '../io/VersionedSerializer.ts';
4
- import {NoPayload, type VerbHandler, type VerbHandlerContext, Verbs} from '../net/Action.ts';
5
- import {type Address, addressAsString, compareAddress, fromIpAddress} from '../cluster/Address.ts';
6
- import {type Comparator, composeComparators, ObjectMap, ObjectSet} from '../utils/collections.ts';
7
- import {type MessageDelivery, MessagingService, type Messenger} from './Messenger.ts';
8
- import {encodedUtfLength} from '../io/util.ts';
9
- import {getByAddress} from '../utils/network.ts';
10
- import {computeUnsignedVIntSize} from '../utils/vint.ts';
11
- import {addressSerializer} from '../net/AddressSerializer.ts';
12
- import {EndpointState, EndpointStateSerializer} from './EndpointState.ts';
13
- import {EMPTY_VERSION, HeartbeatState} from './HeartbeatState.ts';
14
- import {globalClock} from '../utils/clock.ts';
15
- import {type ApplicationState} from './ApplicationState.ts';
16
- import {type VersionedValue, VersionedValueDelimiter} from './VersionedValue.ts';
17
- import {failureDetector} from './FailureDetector.ts';
18
- import type {RequestCallback} from '../net/RequestCallbacks.ts';
19
- import {convert} from '../config/Duration.ts';
20
-
21
- function getMaxEndpointStateVersion(state: EndpointState): number {
22
- let maxVersion = state.heartbeat.version;
23
- for (const [key, value] of state.states()) {
24
- maxVersion = Math.max(maxVersion, value.version);
25
- }
26
- return maxVersion;
27
- }
28
-
29
- type GossipResult = { seed?: true };
30
- type GossipStatus = '' | 'LEFT' | 'removing' | 'removed' | 'hibernate' | 'shutdown';
31
- const DEAD_STATES = new Set<GossipStatus>(['removing', 'removed', 'hibernate', 'LEFT']);
32
-
33
- function getGossipStatus(state?: Readonly<EndpointState>): GossipStatus {
34
- if (state === undefined) {
35
- return '';
36
- }
37
-
38
- let versionedValue = state.getApplicationState('STATUS');
39
- if (versionedValue === undefined) {
40
- versionedValue = state.getApplicationState('_STATUS');
41
- if (versionedValue === undefined) {
42
- return '';
43
- }
44
- }
45
- const value = versionedValue.value;
46
- const values = value.split(VersionedValueDelimiter);
47
- if (values.length === 0) {
48
- throw new Error(`Invalid status value ${value}`);
49
- }
50
- return values[0] as GossipStatus;
51
- }
52
-
53
- const BOOTSTRAPPING_STATUS = new Set(['BOOT', 'BOOT_REPLACE']);
54
-
55
- function stateOrder(): Comparator<[endpoint: Address, state: EndpointState]> {
56
- const cache = new class extends ObjectMap<Address, EndpointState> {
57
- lookup([key, value]: [Address, EndpointState]) {
58
- if (this.has(key)) {
59
- return this.get(key);
60
- }
61
- this.set(key, value.copy());
62
- return this.get(key);
63
- }
64
- }();
65
-
66
-
67
- return composeComparators<[endpoint: Address, state: EndpointState]>(
68
- (e1, e2) => {
69
- const status1 = getGossipStatus(cache.lookup(e1));
70
- const status2 = getGossipStatus(cache.lookup(e2));
71
- if (status1 === status2 || BOOTSTRAPPING_STATUS.has(status1) && BOOTSTRAPPING_STATUS.has(status2)) {
72
- return 0;
73
- }
74
- if (BOOTSTRAPPING_STATUS.has(status1)) {
75
- return 1;
76
- }
77
- if (BOOTSTRAPPING_STATUS.has(status2)) {
78
- return -1;
79
- }
80
- return 0;
81
- },
82
- (e1, e2) => {
83
- const generation1 = cache.lookup(e1).heartbeat.generation;
84
- const generation2 = cache.lookup(e2).heartbeat.generation;
85
- return (generation1 === generation2) ? 0 : ((generation1 > generation2) ? 1 : -1);
86
- },
87
- ([a1], [a2]) => {
88
- return compareAddress(a1, a2);
89
- });
90
- }
91
-
92
- const logger = getLogger('gossiper');
93
-
94
- export type EndpointStateSubscriber = {
95
- onJoin(endpoint: Address, state: EndpointState): void
96
- onChange(endpoint: Address, state: ApplicationState, value: VersionedValue): void
97
- onAlive(endpoint: Address, state: EndpointState): void
98
- onDead(endpoint: Address, state: EndpointState): void
99
- onRemove(endpoint: Address): void
100
- onRestart(endpoint: Address, state: EndpointState): void
101
- }
102
-
103
- const MAX_GENERATION_DIFFERENCE = 86400 * 365;
104
- const QUARANTINE_DELAY = 30 * 1000 * 2;
105
-
106
- const VERY_LONG_TIME_MS = 259200000;
107
- const aVeryLongTime = BigInt(VERY_LONG_TIME_MS);
108
-
109
- function computeExpireTime() {
110
- return globalClock.epochTime() + aVeryLongTime;
111
- }
112
-
113
- class Gossiper {
114
-
115
- private static readonly disableEndpointRemoval = false;
116
-
117
- private gossipTaskSetInterval: ReturnType<typeof setInterval>;
118
-
119
- private readonly fatClientTimeout: bigint;
120
-
121
- private readonly subscribers = new Array<EndpointStateSubscriber>();
122
- private readonly liveEndpoints: Set<Address> = new ObjectSet<Address>();
123
- private readonly unreachableEndpoints: Map<Address, bigint> = new ObjectMap<Address, bigint>();
124
-
125
- private readonly seeds: Set<Address> = new ObjectSet<Address>();
126
-
127
- // key address:port -> value EndpointState
128
- private readonly endpointState: Map<Address, EndpointState> = new ObjectMap<Address, EndpointState>();
129
-
130
- private readonly justRemovedEndpoints: Map<Address, bigint> = new ObjectMap();
131
- private readonly expiredEndpoints: Map<Address, bigint> = new Map();
132
- lastProcessedTimeMs: bigint = globalClock.epochTime();
133
- firstSynSendAt?: bigint;
134
- readonly messaging: Messenger;
135
-
136
- private readonly config: { broadcastAddress: Address, clusterName: string };
137
- private readonly messageFactory: MessageFactory;
138
-
139
- constructor(config: { localAddress: Address, broadcastAddress: Address, clusterName: string }) {
140
- const context = {
141
- gossiper: () => this,
142
- messaging: () => this.messaging,
143
- messageFactory: () => this.messageFactory
144
- };
145
-
146
- this.fatClientTimeout = BigInt(QUARANTINE_DELAY / 3);
147
- this.config = config;
148
- this.messageFactory = new MessageFactory(config.broadcastAddress);
149
- this.messaging = new MessagingService(config.localAddress, context);
150
- }
151
-
152
- get endpointStates(): ReadonlyMap<Address, EndpointState> {
153
- return this.endpointState;
154
- }
155
-
156
- register(subscriber: EndpointStateSubscriber): void {
157
- this.subscribers.push(subscriber);
158
- }
159
-
160
- examineGossiper(digests: ReadonlyArray<GossipDigest>): GossipDigestAck {
161
- if (digests.length === 0) {
162
- throw new Error(`examineGossiper: empty digests`);
163
- }
164
-
165
- const delta = {
166
- digests: new Array<GossipDigest>(),
167
- states: new ObjectMap<Address, EndpointState>()
168
- };
169
- for (const digest of digests) {
170
- const {endpoint, generation: remoteGeneration, maxVersion: maxRemoteVersion} = digest;
171
-
172
- const state = this.endpointState.get(endpoint);
173
- if (state) {
174
- const localGeneration = state.heartbeat.generation;
175
- const maxLocalVersion = getMaxEndpointStateVersion(state);
176
-
177
- if (remoteGeneration === localGeneration && maxRemoteVersion === maxLocalVersion) {
178
- continue;
179
- }
180
- // request all from the gossiper
181
- if (remoteGeneration > localGeneration) {
182
- this.requestAll(digest, delta.digests, remoteGeneration);
183
- } else if (remoteGeneration < localGeneration) {
184
- // send all data with local generation > -1
185
- this.sendAll(digest, delta.states, EMPTY_VERSION);
186
- } else if (remoteGeneration === localGeneration) {
187
- if (maxRemoteVersion > maxLocalVersion) {
188
- delta.digests.push({endpoint, generation: remoteGeneration, maxVersion: maxLocalVersion});
189
- } else if (maxRemoteVersion < maxLocalVersion) {
190
- this.sendAll(digest, delta.states, maxRemoteVersion);
191
- }
192
- }
193
- } else {
194
- this.requestAll(digest, delta.digests, remoteGeneration);
195
- }
196
- }
197
- return delta;
198
-
199
- }
200
-
201
- applyStateLocally(states: ReadonlyMap<Address, EndpointState>): void {
202
- for (const [endpoint, remoteState] of Array.from(states.entries()).sort(stateOrder())) {
203
- // todo: skip if broadcast address
204
-
205
- if (this.justRemovedEndpoints.has(endpoint)) {
206
- continue;
207
- }
208
- const localState = this.endpointState.get(endpoint);
209
-
210
- if (localState != null) {
211
- const localGeneration = localState.heartbeat.generation;
212
- const remoteGeneration = remoteState.heartbeat.generation;
213
- const localTime = Number(globalClock.epochTime() / 1000n);
214
- if (logger.enabledFor('debug')) {
215
- logger.debug(`${addressAsString(endpoint)} local generation ${localGeneration}, remote generation ${remoteGeneration}`);
216
- }
217
- if (remoteGeneration > localTime + MAX_GENERATION_DIFFERENCE) {
218
- logger.warn(`received an invalid gossip generation for peer ${addressAsString(endpoint)}; local time = ${localTime}, remote generation = ${remoteGeneration}`);
219
- }
220
- else if (remoteGeneration > localGeneration) {
221
- if (logger.enabledFor('debug')) {
222
- logger.debug(`Updating heartbeat state generation to ${remoteGeneration} from ${localGeneration} for ${addressAsString(endpoint)}`);
223
- }
224
- this.handleMajorStateChange(endpoint, remoteState);
225
- } else if (remoteGeneration === localGeneration) {
226
- const localMaxVersion = getMaxEndpointStateVersion(localState);
227
- const remoteMaxVersion = getMaxEndpointStateVersion(remoteState);
228
- if (remoteMaxVersion > localMaxVersion) {
229
- this.applyNewStates(endpoint, localState, remoteState);
230
- } else if (logger.enabledFor('debug')) {
231
- logger.debug(`Ignoring remote version ${remoteMaxVersion} <= ${localMaxVersion} for ${addressAsString(endpoint)}`);
232
- }
233
- if (!localState.isAlive && !this.isDeadState(localState)) {
234
- this.markAlive(endpoint, localState);
235
- }
236
- } else {
237
- if (logger.enabledFor('debug')) {
238
- logger.debug(`Ignoring remote generation ${remoteGeneration} < ${localGeneration} for ${addressAsString(endpoint)} `);
239
- }
240
- }
241
- }
242
- else {
243
- failureDetector.report(endpoint);
244
- this.handleMajorStateChange(endpoint, remoteState);
245
- }
246
- }
247
- }
248
-
249
- private applyNewStates(endpoint: Address, localState: EndpointState, remoteState: EndpointState): void {
250
- const oldVersion = localState.heartbeat.version;
251
-
252
- localState.heartbeat = remoteState.heartbeat;
253
- if (logger.enabledFor('debug')) {
254
- logger.debug(`Updating heartbeat state version to ${localState.heartbeat.version} from ${oldVersion} for ${addressAsString(endpoint)} ...`);
255
- }
256
-
257
- const remoteStates = Array.from(remoteState.states());
258
- if (remoteState.heartbeat.generation !== localState.heartbeat.generation) {
259
- throw new Error();
260
- }
261
-
262
- const updatedStates = remoteStates.filter(([key, value]) => {
263
- const local = localState.getApplicationState(key);
264
- return local === undefined || local.version < value.version;
265
- });
266
-
267
- if (logger.enabledFor('debug') && updatedStates.length > 0) {
268
- for (const [key, value] of updatedStates) {
269
- logger.debug(`Updating ${key} state version to ${value.version} for ${addressAsString(endpoint)}`);
270
- }
271
- }
272
-
273
- localState.addApplicationStates(updatedStates);
274
- // localState.remove
275
-
276
- for (const [key, value] of updatedStates) {
277
- switch (key) {
278
- default:
279
- continue;
280
- case '_STATUS':
281
- if (localState.hasApplicationState('STATUS')) {
282
- continue;
283
- }
284
- case 'STATUS': {
285
-
286
- }
287
- }
288
- this.doOnChangeNotification(endpoint, key, value);
289
- }
290
-
291
- for (const [key, value] of updatedStates) {
292
- switch (key) {
293
- case '_STATUS':
294
- case 'STATUS':
295
- continue;
296
- }
297
-
298
- if ((key === '_INTERNAL_IP' && localState.hasApplicationState('INTERNAL_ADDRESS'))
299
- || (key === '_RPC_ADDRESS' && localState.hasApplicationState('NATIVE_ADDRESS'))) {
300
- continue;
301
- }
302
- this.doOnChangeNotification(endpoint, key, value);
303
- }
304
-
305
- }
306
-
307
- public isGossipOnlyMember(endpoint: Address): boolean {
308
- const state = this.endpointState.get(endpoint);
309
- if (state === undefined) {
310
- return false;
311
- }
312
- const status = getGossipStatus(state);
313
- if (status === 'removing' || status === 'removed') {
314
- return true;
315
- }
316
- return !this.isDeadState(state) && !(false/*todo check current metadata*/);
317
- }
318
-
319
- private async doStatusCheck() {
320
- if (logger.enabledFor('trace')) {
321
- logger.debug(`Performing status check ...`);
322
- }
323
-
324
- const now = globalClock.epochTime();
325
- const nowNs = globalClock.nanoTime();
326
-
327
- for (const endpoint of this.endpointStates.keys()) {
328
- if (compareAddress(this.config.broadcastAddress, endpoint) === 0) {
329
- continue;
330
- }
331
- failureDetector.interpret(endpoint);
332
- const state = this.endpointState.get(endpoint);
333
- if (state !== undefined) {
334
- if (this.isGossipOnlyMember(endpoint)
335
- && !this.justRemovedEndpoints.has(endpoint)
336
- && convert('milliseconds', nowNs - state.timestamp , 'nanoseconds') > this.fatClientTimeout) {
337
- logger.info(`FatClient ${addressAsString(endpoint)} has been silent for ${this.fatClientTimeout}ms, removing from gossip`);
338
-
339
- await new Promise<void>((resolve, reject) => {
340
- setImmediate(() => {
341
- try {
342
- if (!this.isGossipOnlyMember(endpoint)) {
343
- logger.info(`Race condition marking ${addressAsString(endpoint)} as FatClient; ignoring`);
344
- } else {
345
- this.removeEndpoint(endpoint);
346
- this.evictFromMembership(endpoint);
347
- }
348
- resolve();
349
- } catch (e) {
350
- reject(e);
351
- }
352
- });
353
- });
354
- }
355
- const expireTime = this.getExpireTimeForEndpoint(endpoint);
356
- if (!state.isAlive && (now > expireTime) && (!false /*metadata*/)) {
357
- if (logger.enabledFor('debug')) {
358
- logger.debug(`time is expiring for endpoint ${addressAsString(endpoint)}: ${expireTime}`);
359
- }
360
- await new Promise<void>((resolve, reject) => {
361
- setImmediate(() => {
362
- try {
363
- this.evictFromMembership(endpoint);
364
- resolve();
365
- } catch (e) {
366
- reject(e);
367
- }
368
- });
369
- });
370
- }
371
- }
372
- }
373
- if (this.justRemovedEndpoints.size !== 0) {
374
- for (const [endpoint, timestamp] of this.justRemovedEndpoints) {
375
- if ((now - timestamp) > QUARANTINE_DELAY) {
376
- logger.debug(`${QUARANTINE_DELAY} elapsed, ${addressAsString(endpoint)} quarantine over`);
377
- this.justRemovedEndpoints.delete(endpoint);
378
- }
379
- }
380
- }
381
- }
382
-
383
- public doOnChangeNotification(endpoint: Address, state: ApplicationState, value: VersionedValue): void {
384
- for (const subscriber of this.subscribers) {
385
- subscriber.onChange(endpoint, state, value);
386
- }
387
-
388
- }
389
- private handleMajorStateChange(endpoint: Address, remoteState: EndpointState): void {
390
- const localState = this.endpointState.get(endpoint);
391
- if (!this.isDeadState(remoteState)) {
392
- if (localState !== undefined) {
393
- logger.info(`Node ${addressAsString(endpoint)} has restarted, now UP`);
394
- }
395
- else {
396
- logger.info(`Node ${addressAsString(endpoint)} is now part of the cluster`);
397
- }
398
- }
399
- if (logger.enabledFor('trace')) {
400
- logger.debug(`Adding endpoint state for ${addressAsString(endpoint)}`);
401
- }
402
- this.endpointState.set(endpoint, remoteState);
403
-
404
- if (localState !== undefined) {
405
- for (const subscriber of this.subscribers) {
406
- subscriber.onRestart(endpoint, localState);
407
- }
408
- }
409
-
410
- if (!this.isDeadState(remoteState)) {
411
- this.markAlive(endpoint, remoteState);
412
- }
413
- else {
414
- if (logger.enabledFor('debug')) {
415
- logger.debug(`Not marking ${addressAsString(endpoint)} alive due to dead state`);
416
- }
417
- this.markDead(endpoint, remoteState);
418
- }
419
-
420
- for (const subscriber of this.subscribers) {
421
- subscriber.onJoin(endpoint, remoteState);
422
- }
423
-
424
- if (this.isShutdown(endpoint)) {
425
- this.markAsShutdown(endpoint, remoteState);
426
- }
427
- }
428
-
429
- private markAlive(endpoint: Address, localState: EndpointState): void {
430
- localState.markAsDead();
431
-
432
- const echo = this.messageFactory.out(Verbs.ECHO_REQ, NoPayload.instance);
433
- if (logger.enabledFor('debug')) {
434
- logger.debug(`Sending ECHO_REQ to ${addressAsString(endpoint)}`);
435
- }
436
-
437
- const handler: RequestCallback<unknown> = (msg) => {
438
- if (logger.enabledFor('debug')) {
439
- logger.debug(`Received ECHO_RSP from ${addressAsString(endpoint)}`);
440
- }
441
- this.realMarkAlive(endpoint, localState);
442
- }
443
- this.messaging.sendWithCallback(echo, endpoint, handler);
444
- }
445
-
446
- private realMarkAlive(endpoint: Address, localState: EndpointState): void {
447
- if (logger.enabledFor('trace')) {
448
- logger.debug(`marking as alive ${addressAsString(endpoint)}`);
449
- }
450
- localState.markAsAlive();
451
- localState.updateTimestamp();
452
- this.liveEndpoints.add(endpoint);
453
- this.unreachableEndpoints.delete(endpoint);
454
- this.expiredEndpoints.delete(endpoint);
455
- if (logger.enabledFor('debug')) {
456
- logger.debug(`removing expire time for ${addressAsString(endpoint)}`);
457
- }
458
- logger.info(`Address ${addressAsString(endpoint)} is now UP`);
459
- for (const subscriber of this.subscribers) {
460
- subscriber.onAlive(endpoint, localState);
461
- }
462
- if (logger.enabledFor('trace')) {
463
- logger.debug(`Notified {}`, this.subscribers);
464
- }
465
- }
466
-
467
- markDead(endpoint: Address, localState: EndpointState): void {
468
- if (logger.enabledFor('trace')) {
469
- logger.debug(`marking as down ${addressAsString(endpoint)}`);
470
- }
471
- this.silentlyMarkDead(endpoint, localState);
472
- logger.info(`Address ${addressAsString(endpoint)} is now DOWN`);
473
- // todo: check if the node is registered
474
- for (const subscriber of this.subscribers) {
475
- subscriber.onDead(endpoint, localState);
476
- }
477
-
478
- if (logger.enabledFor('trace')) {
479
- logger.debug(`Notified ${this.subscribers}`);
480
- }
481
- }
482
-
483
- private silentlyMarkDead(endpoint: Address, localState: EndpointState): void {
484
- localState.markAsDead();
485
- if (!Gossiper.disableEndpointRemoval) {
486
- this.liveEndpoints.delete(endpoint);
487
- this.unreachableEndpoints.set(endpoint, globalClock.nanoTime());
488
- }
489
- }
490
-
491
- private isShutdown(endpoint: Address) {
492
- const state = this.endpointState.get(endpoint);
493
- if (state === undefined) {
494
- return false;
495
- }
496
- return getGossipStatus(state) === 'shutdown';
497
- }
498
-
499
- private requestAll(digest: GossipDigest, digests: GossipDigest[], remoteGeneration: number): void {
500
- digests.push({endpoint: digest.endpoint, generation: remoteGeneration, maxVersion: 0});
501
- if (logger.enabledFor('debug')) {
502
- logger.debug(`requestAll for ${addressAsString(digest.endpoint)}`);
503
- }
504
- }
505
-
506
- private sendAll(digest: GossipDigest, states: Map<Address, EndpointState>, maxRemoteVersion: number): void {
507
- const localState: EndpointState = this.getStatesForVersionsBiggerThan(digest.endpoint, maxRemoteVersion);
508
- if (localState) {
509
- states.set(digest.endpoint, localState);
510
- }
511
- }
512
-
513
- examineShadowState(): Map<Address, EndpointState> {
514
- const map = new ObjectMap<Address, EndpointState>();
515
- for (const [endpoint, value] of this.endpointState) {
516
- const state = value.copy()
517
- if (state.isEmptyWithoutStatus) {
518
-
519
- }
520
- }
521
- return map;
522
- }
523
-
524
- private getExpireTimeForEndpoint(endpoint: Address): bigint {
525
- const storedTime = this.expiredEndpoints.get(endpoint);
526
- if (storedTime !== undefined) {
527
- return storedTime;
528
- }
529
- return computeExpireTime();
530
- }
531
-
532
- getStatesForVersionsBiggerThan(endpoint: Address, version: number): EndpointState | undefined {
533
- const state = this.endpointState.get(endpoint);
534
- let requestedState: EndpointState | undefined;
535
- if (state) {
536
- const {generation: localHbGeneration, version: localHbVersion} = state.heartbeat;
537
- if (localHbVersion > version) {
538
- requestedState = new EndpointState(new HeartbeatState(localHbGeneration, localHbVersion));
539
- if (logger.enabledFor('debug')) {
540
- logger.debug(`Local heartbeat version ${localHbVersion} greater than ${version} for ${addressAsString(endpoint)}`);
541
- }
542
- }
543
-
544
- const states = new Map<ApplicationState, VersionedValue>();
545
- /* Accumulate all application states whose versions are greater than "version" variable */
546
- for (const [key, value] of state.states()) {
547
- if (value.version > version) {
548
- if (requestedState === undefined) {
549
- requestedState = new EndpointState(new HeartbeatState(localHbGeneration, localHbVersion));
550
- }
551
- logger.debug(`Adding state ${key} with version ${value.version} for val ${value.value}`);
552
- states.set(key, value);
553
- }
554
- }
555
- if (requestedState) {
556
- requestedState.addApplicationStates(Array.from(states.entries()));
557
- }
558
- }
559
- return requestedState;
560
- }
561
-
562
- notifyFailureDetector(remoteState: ReadonlyMap<Address, EndpointState>): void {
563
- for (const [endpoint, state] of remoteState) {
564
- this.notifyFailureDetectorFor(endpoint, state);
565
- }
566
- }
567
-
568
- private notifyFailureDetectorFor(endpoint: Address, remoteState?: EndpointState): void {
569
- if (remoteState === undefined) {
570
- return;
571
- }
572
-
573
- const localState = this.endpointState.get(endpoint);
574
-
575
- if (localState !== undefined) {
576
- const fd = failureDetector;
577
-
578
- const localGeneration = localState.heartbeat.generation;
579
- const remoteGeneration = remoteState.heartbeat.generation;
580
- if (remoteGeneration > localGeneration) {
581
- localState.updateTimestamp();
582
- // this endpoint was dead and the generation changed, this indicates a re-boot
583
- if (!localState.isAlive) {
584
- if (logger.enabledFor('debug')) {
585
- logger.debug(`Clearing interval times for ${addressAsString(endpoint)} due to generation change`);
586
- }
587
- fd.remove(endpoint);
588
- }
589
- fd.report(endpoint);
590
- return;
591
- }
592
- if (remoteGeneration === localGeneration) {
593
- const localVersion = getMaxEndpointStateVersion(localState);
594
- const remoteVersion = remoteState.heartbeat.version;
595
- if (remoteVersion > localVersion) {
596
- localState.updateTimestamp();
597
- // just a version change
598
- fd.report(endpoint);
599
- }
600
- }
601
- }
602
- }
603
-
604
-
605
- start(generationNumber: number, mergeLocalStates = false) {
606
- this.buildSeedList();
607
- this.maybeInitializeLocalState(generationNumber);
608
- this.gossipTaskSetInterval = setInterval(() => {
609
- this.task();
610
- }, 1000)
611
- }
612
-
613
- stop() {
614
- if (this.gossipTaskSetInterval) {
615
- clearInterval(this.gossipTaskSetInterval);
616
- this.gossipTaskSetInterval = undefined;
617
- }
618
- }
619
-
620
- get enabled() {
621
- return this.gossipTaskSetInterval !== undefined;
622
- }
623
-
624
- private buildSeedList(): void {
625
- this.seeds.add(fromIpAddress(getByAddress(undefined, 0x7f000001), 7000));
626
- }
627
-
628
- private maybeInitializeLocalState(generationNumber: number): void {
629
- const heartbeat = new HeartbeatState(generationNumber);
630
- const localState = new EndpointState(heartbeat);
631
- localState.markAsAlive();
632
- this.endpointState.set(this.config.broadcastAddress, localState);
633
- }
634
-
635
-
636
- async task() {
637
- this.endpointState.get(this.config.broadcastAddress).heartbeat.updateHeartbeatVersion();
638
- if (logger.enabledFor('trace')) {
639
- logger.debug(`My heartbeat is now ${this.endpointState.get(this.config.broadcastAddress).heartbeat.version}`);
640
- }
641
- const digests: GossipDigest[] = [];
642
- this.makeGossipDigest(digests);
643
- if (digests.length > 0) {
644
- const cluster = this.config.clusterName;
645
- const partitioner = "org.apache.cassandra.dht.Murmur3Partitioner";
646
- const digestSynMessage = {cluster, partitioner, digests};
647
- const message = this.messageFactory.out(Verbs.GOSSIP_DIGEST_SYN, digestSynMessage)
648
- let gossipedWith: GossipResult = this.doGossipToLiveMember(message);
649
-
650
- this.maybeGossipToUnreachableMember(message);
651
-
652
- if (!gossipedWith?.seed || this.liveEndpoints.size < this.seeds.size) {
653
- gossipedWith = {...this.maybeGossipToSeed(message), ...gossipedWith};
654
- }
655
-
656
- await this.doStatusCheck();
657
- }
658
- }
659
-
660
- markAsShutdown(endpoint: Address, remoteState: EndpointState): void {
661
- const endpointState = this.endpointState.get(endpoint);
662
- if (endpointState === undefined || endpointState.isStateEmpty) {
663
- return;
664
- }
665
- const shutdown = remoteState.getApplicationState('STATUS');
666
- if (shutdown === undefined) {
667
- throw new Error(`assertion failed: remote shutdown sent but not with a shutdown status? ${remoteState}`);
668
- }
669
- remoteState.heartbeat.forceHighestPossibleVersion()
670
- this.endpointState.set(endpoint, remoteState);
671
- this.markDead(endpoint, remoteState);
672
- failureDetector.forceConviction(endpoint);
673
- for (const subscriber of this.subscribers) {
674
- subscriber.onChange(endpoint, 'STATUS', shutdown);
675
- }
676
- if (logger.enabledFor('debug')) {
677
- logger.debug(`Marked ${addressAsString(endpoint)} as shutdown`);
678
- }
679
- }
680
-
681
- evictFromMembership(endpoint: Address): void {
682
- this.unreachableEndpoints.delete(endpoint);
683
- this.endpointState.delete(endpoint);
684
- this.expiredEndpoints.delete(endpoint);
685
- failureDetector.remove(endpoint);
686
- this.quarantineEndpoint(endpoint);
687
- if (logger.enabledFor('debug')) {
688
- logger.debug(`Evicting ${addressAsString(endpoint)} from membership`);
689
- }
690
- }
691
-
692
- removeEndpoint(endpoint: Address): void {
693
-
694
- for (const subscriber of this.subscribers) {
695
- subscriber.onRemove(endpoint);
696
- }
697
-
698
- if (this.seeds.has(endpoint)) {
699
- this.buildSeedList();
700
- this.seeds.delete(endpoint);
701
- logger.info(`removed ${addressAsString(endpoint)} from seeds, updated seed list = ${this.seeds}`);
702
- if (this.seeds.size === 0) {
703
- logger.warn(`seeds list is empty`);
704
- }
705
- }
706
-
707
- if (Gossiper.disableEndpointRemoval) {
708
- return;
709
- }
710
-
711
- this.liveEndpoints.delete(endpoint);
712
- this.unreachableEndpoints.delete(endpoint);
713
- this.quarantineEndpoint(endpoint);
714
- this.messaging.closeOutbound(endpoint);
715
- this.messaging.removeInbound(endpoint);
716
- logger.debug(`removing endpoint`);
717
- }
718
-
719
- private quarantineEndpoint(endpoint: Address, expirationMs?: bigint): void {
720
- if (Gossiper.disableEndpointRemoval) {
721
- return;
722
- }
723
-
724
- expirationMs ??= globalClock.epochTime();
725
- this.justRemovedEndpoints.set(endpoint, expirationMs);
726
-
727
-
728
- }
729
-
730
- makeGossipDigest(digests: GossipDigest[]): void {
731
- let generation: number;
732
- let maxVersion: number;
733
- for (const [endpoint, state] of this.endpointState) {
734
- if (state) {
735
- generation = state.heartbeat.generation;
736
- maxVersion = getMaxEndpointStateVersion(state);
737
- } else {
738
- generation = 0;
739
- maxVersion = 0;
740
- }
741
- digests.push({endpoint, generation, maxVersion});
742
- }
743
- if (logger.enabledFor('trace')) {
744
- logger.debug(`Gossip Digests are ${JSON.stringify(digests)}`);
745
- }
746
- }
747
-
748
- private doGossipToLiveMember(message: Message<GossipDigestSyn>): GossipResult {
749
- const size = this.liveEndpoints.size;
750
- if (size === 0) {
751
- return {};
752
- }
753
- return this.sendGossip(message, this.liveEndpoints)
754
- }
755
-
756
- private isDeadState(endpoint?: Readonly<EndpointState>): boolean {
757
- const status = getGossipStatus(endpoint)
758
- if (status === '') {
759
- return false;
760
- }
761
- return DEAD_STATES.has(status);
762
- }
763
-
764
- private maybeGossipToUnreachableMember(message: Message<GossipDigestSyn>): void {
765
- const liveCount = this.liveEndpoints.size;
766
- const unreachableCount = this.unreachableEndpoints.size;
767
- if (unreachableCount > 0) {
768
- const prob = unreachableCount / (liveCount + 1);
769
- if (prob > Math.random()) {
770
- const members = Array.from(this.unreachableEndpoints.keys())
771
- .filter(ep => !this.isDeadState(this.endpointState.get(ep)));
772
- this.sendGossip(message, new ObjectSet<Address>(members));
773
- }
774
- }
775
- }
776
-
777
- private maybeGossipToSeed(message: Message<GossipDigestSyn>): GossipResult {
778
- const size = this.seeds.size;
779
- let result: GossipResult = {};
780
- if (size > 0) {
781
- if (size === 1 && this.seeds.has(this.config.broadcastAddress)) {
782
- return result;
783
- }
784
-
785
- if (this.liveEndpoints.size == 0) {
786
- result = this.sendGossip(message, this.seeds);
787
- } else {
788
- const prob = size / (this.liveEndpoints.size + this.unreachableEndpoints.size);
789
- if (prob >= Math.random()) {
790
- result = this.sendGossip(message, this.seeds);
791
- }
792
- }
793
- }
794
- }
795
-
796
- private sendGossip(message: Message<GossipDigestSyn>, epSet: Set<Address>): GossipResult {
797
- const endpoints = Array.from(epSet);
798
- const size = endpoints.length;
799
- if (size < 1) {
800
- return {};
801
- }
802
- const index = size === 1 ? 0 : Math.floor(Math.random() * size);
803
- const to = endpoints[index];
804
- if (logger.enabledFor('trace')) {
805
- logger.debug(`Sending a GossipDigestSyn to ${addressAsString(to)} ...`);
806
- }
807
- if (this.firstSynSendAt === undefined) {
808
- this.firstSynSendAt = globalClock.nanoTime();
809
- }
810
- this.messaging.send(message, to);
811
- const result: GossipResult = {};
812
- if (this.seeds.has(to)) {
813
- result.seed = true;
814
- }
815
- return result;
816
-
817
- }
818
- }
819
-
820
- export type {Gossiper}
821
- export type GossiperConfig = { localAddress: Address, broadcastAddress: Address, clusterName: string }
822
-
823
- export function create(config: GossiperConfig): Gossiper {
824
- return new Gossiper(config);
825
- }
826
-
827
-
828
- export type GossipDigest = {
829
- readonly endpoint: Address,
830
- readonly generation: number,
831
- readonly maxVersion: number,
832
- };
833
-
834
- const GossipDigestSerializer = new (class implements VersionedSerializer<GossipDigest> {
835
- serializedSize(value: GossipDigest, version: number): number {
836
- let size = addressSerializer.serializedSize(value.endpoint, version);
837
- size += 4; // generation
838
- size += 4; // maxVersion
839
- return size;
840
- }
841
-
842
- serialize(value: GossipDigest, output: DataOutput, version?: number) {
843
- addressSerializer.serialize(value.endpoint, output, version);
844
- output.writeInt32(value.generation);
845
- output.writeInt32(value.maxVersion);
846
- }
847
-
848
- deserialize(input: DataInput, version?: number): GossipDigest {
849
- const endpoint = addressSerializer.deserialize(input, version);
850
- const generation = input.readInt32();
851
- const maxVersion = input.readInt32();
852
- return {endpoint, generation, maxVersion};
853
- }
854
- });
855
-
856
- function serializeGossipDigestList(digests: readonly GossipDigest[], output: DataOutput, version?: number): void {
857
- output.writeInt32(digests.length);
858
- for (const digest of digests) {
859
- GossipDigestSerializer.serialize(digest, output, version);
860
- }
861
- }
862
-
863
- function deserializeGossipDigestList(input: DataInput, version?: number): readonly GossipDigest[] {
864
- const size = input.readInt32();
865
- const digests = new Array<GossipDigest>(size);
866
- for (let i = 0; i < size; i++) {
867
- digests[i] = GossipDigestSerializer.deserialize(input, version);
868
- }
869
- return digests;
870
- }
871
-
872
- function serializedSizeGossipDigestList(digests: readonly GossipDigest[], version?: number): number {
873
- let size = 4;
874
- for (const digest of digests) {
875
- size += GossipDigestSerializer.serializedSize(digest, version);
876
- }
877
- return size;
878
- }
879
-
880
- export type GossipDigestSyn = { readonly cluster: string, readonly digests: ReadonlyArray<GossipDigest> };
881
-
882
- export const GossipDigestSynSerializer = new (class implements VersionedSerializer<GossipDigestSyn> {
883
- serialize(value: GossipDigestSyn, output: DataOutput, version?: number): void {
884
- output.writeUtf8String(value.cluster);
885
- output.writeUtf8String("org.apache.cassandra.dht.Murmur3Partitioner");
886
- if (version >= 14) {
887
- output.writeUnsignedVInt(0);
888
- }
889
- serializeGossipDigestList(value.digests, output, version);
890
-
891
- }
892
-
893
- deserialize(input: DataInput, version?: number): GossipDigestSyn {
894
- const clusterId = input.readUtf8String();
895
- const _ = input.readUtf8String();
896
- const metadataId = (version >= 14) ? input.readUnsignedVInt32() : 0;
897
- const digests = deserializeGossipDigestList(input, version);
898
- return {cluster: clusterId, digests};
899
- }
900
-
901
- serializedSize(value: GossipDigestSyn, version?: number): number {
902
- let size = 2 + encodedUtfLength(value.cluster);
903
- size += 2 + encodedUtfLength("org.apache.cassandra.dht.Murmur3Partitioner");
904
- if (version >= 14) {
905
- size += computeUnsignedVIntSize(0);
906
- }
907
- size += serializedSizeGossipDigestList(value.digests, version);
908
- return size;
909
- }
910
-
911
- });
912
-
913
- class GossipVerbHandler<T> implements VerbHandler<T> {
914
- protected readonly gossiper: Gossiper;
915
-
916
- constructor(context: { gossiper: () => Gossiper }) {
917
- this.gossiper = context.gossiper();
918
- }
919
-
920
- doVerb(message: Message<T>) {
921
- this.gossiper.lastProcessedTimeMs = message.creationTime;
922
- }
923
- }
924
-
925
- export function gossipDigestSynHandler(ctx: VerbHandlerContext): VerbHandler<GossipDigestSyn> {
926
- return new class extends GossipVerbHandler<GossipDigestSyn> {
927
- doVerb(message: Message<GossipDigestSyn>): void {
928
- const from = message.from;
929
- if (logger.enabledFor('debug')) {
930
- logger.debug(`Received a GossipDigestSyn from ${addressAsString(from)}`);
931
- }
932
- if (!this.gossiper.enabled) {
933
- logger.debug(`Ignoring GossipDigestSyn because gossiper is disabled`);
934
- return;
935
- }
936
- const {cluster, digests} = message.payload;
937
-
938
- if (!this.gossiper.enabled) {
939
- if (digests.length > 0) {
940
- return;
941
- }
942
- ctx.messaging().send(ctx.messageFactory().out(Verbs.GOSSIP_DIGEST_ACK, {
943
- digests: [],
944
- states: new Map()
945
- }), from);
946
- return;
947
- }
948
-
949
- if (logger.enabledFor('debug')) {
950
- logger.debug(`Gossip syn digests are: ${digests}`);
951
- }
952
-
953
- const gossipDigestAckMessage = (digests.length === 0)
954
- ? this.createShadowReply()
955
- : this.createReply(digests);
956
- if (logger.enabledFor('debug')) {
957
- logger.debug(`Sending a GossipDigestAck to ${addressAsString(from)}`);
958
- }
959
- ctx.messaging().send(gossipDigestAckMessage, from);
960
-
961
- super.doVerb(message);
962
- }
963
-
964
- createReply(digests: ReadonlyArray<GossipDigest>): Message<GossipDigestAck> {
965
- const delta = ctx.gossiper().examineGossiper(digests);
966
- logger.debug(`Sending ${delta.digests.length} digests and ${delta.states.size} deltas`);
967
- return ctx.messageFactory().out(Verbs.GOSSIP_DIGEST_ACK, delta);
968
- }
969
-
970
- createShadowReply(): Message<GossipDigestAck> {
971
- const deltas: Map<Address, EndpointState> = ctx.gossiper().examineShadowState();
972
- logger.debug(`Sending 0 digests and ${deltas.size} deltas`);
973
- return ctx.messageFactory().out(Verbs.GOSSIP_DIGEST_ACK, {digests: [], states: deltas});
974
- }
975
- }(ctx);
976
- }
977
-
978
- export type GossipDigestAck = Readonly<{
979
- digests: ReadonlyArray<GossipDigest>,
980
- states: ReadonlyMap<Address, EndpointState>,
981
- }>
982
-
983
-
984
- export const GossipDigestAckSerializer = new (class implements VersionedSerializer<GossipDigestAck> {
985
- serialize(value: GossipDigestAck, output: DataOutput, version: number) {
986
- serializeGossipDigestList(value.digests, output, version);
987
- output.writeInt32(value.states.size);
988
- for (const [address, state] of value.states) {
989
- addressSerializer.serialize(address, output, version);
990
- EndpointStateSerializer.serialize(state, output, version);
991
- }
992
- }
993
-
994
- deserialize(input: DataInput, version: number): GossipDigestAck {
995
- const digests = deserializeGossipDigestList(input, version);
996
- const size = input.readInt32();
997
- const states = new ObjectMap<Address, EndpointState>();
998
- for (let i = 0; i < size; i++) {
999
- const address = addressSerializer.deserialize(input, version);
1000
- const endpointState = EndpointStateSerializer.deserialize(input, version);
1001
- states.set(address, endpointState);
1002
- }
1003
- return {digests, states};
1004
- }
1005
-
1006
- serializedSize(value: GossipDigestAck, version: number): number {
1007
- let size = serializedSizeGossipDigestList(value.digests, version);
1008
- size += 4; // size
1009
- for (const [address, state] of value.states) {
1010
- size += addressSerializer.serializedSize(address, version);
1011
- size += EndpointStateSerializer.serializedSize(state, version);
1012
- }
1013
- return size;
1014
- }
1015
- });
1016
-
1017
- export function gossipDigestAckHandler(ctx: VerbHandlerContext): VerbHandler<GossipDigestAck> {
1018
- return new (class extends GossipVerbHandler<GossipDigestAck> {
1019
- private readonly messageFactory: MessageFactory;
1020
- private readonly messageDelivery: MessageDelivery;
1021
- constructor(ctx: VerbHandlerContext) {
1022
- super(ctx);
1023
- this.messageFactory = ctx.messageFactory();
1024
- this.messageDelivery = ctx.messaging();
1025
- }
1026
-
1027
- doVerb(message: Message<GossipDigestAck>): void {
1028
- const from = message.from;
1029
- if (logger.enabledFor('debug')) {
1030
- logger.debug(`Received a GossipDigestAck from ${addressAsString(from)}`);
1031
- }
1032
- if (!this.gossiper.enabled) {
1033
- logger.debug(`Ignoring GossipDigestAck because gossiper is disabled`);
1034
- return;
1035
- }
1036
-
1037
- const {states, digests} = message.payload;
1038
- if (logger.enabledFor('debug')) {
1039
- logger.debug(`Received ack with ${digests.length} digests and ${states.size} states`);
1040
- }
1041
-
1042
- if (states.size > 0) {
1043
-
1044
- if (this.gossiper.firstSynSendAt === undefined || (globalClock.nanoTime() - this.gossiper.firstSynSendAt) < 0) {
1045
- logger.debug(`Ignoring unrequested GossipDigestAck from ${addressAsString(from)}`);
1046
- return;
1047
- }
1048
- this.gossiper.notifyFailureDetector(states);
1049
- this.gossiper.applyStateLocally(states);
1050
- }
1051
-
1052
- const delta = new ObjectMap<Address, EndpointState>();
1053
- for (const digest of digests) {
1054
- const addr = digest.endpoint;
1055
- const localState = this.gossiper.getStatesForVersionsBiggerThan(addr, digest.maxVersion);
1056
- if (localState) {
1057
- delta.set(addr, localState);
1058
- }
1059
- }
1060
-
1061
- const ack2: Message<GossipDigestAck2> = this.messageFactory.out(Verbs.GOSSIP_DIGEST_ACK2, {states: delta});
1062
- logger.debug(`Sending a GossipDigestAck2 to ${addressAsString(from)}`);
1063
- this.messageDelivery.send(ack2, from);
1064
-
1065
- super.doVerb(message);
1066
- }
1067
- })(ctx);
1068
- }
1069
-
1070
- export type GossipDigestAck2 = Readonly<{ states: ReadonlyMap<Address, EndpointState> }>;
1071
- export const GossipDigestAck2Serializer = new (class implements VersionedSerializer<GossipDigestAck2> {
1072
-
1073
- serializedSize(value: GossipDigestAck2, version: number): number {
1074
- let size = 4;// size
1075
- for (const [address, state] of value.states) {
1076
- size += addressSerializer.serializedSize(address, version);
1077
- size += EndpointStateSerializer.serializedSize(state, version);
1078
- }
1079
- return size;
1080
- }
1081
-
1082
- serialize(value: GossipDigestAck2, output: DataOutput, version: number): void {
1083
- output.writeInt32(value.states.size);
1084
- for (const [address, state] of value.states) {
1085
- addressSerializer.serialize(address, output, version);
1086
- EndpointStateSerializer.serialize(state, output, version);
1087
- }
1088
- }
1089
-
1090
- deserialize(input: DataInput, version: number): GossipDigestAck2 {
1091
- const size = input.readInt32();
1092
- const states = new ObjectMap<Address, EndpointState>();
1093
- for (let i = 0; i < size; i++) {
1094
- const address = addressSerializer.deserialize(input, version);
1095
- const endpointState = EndpointStateSerializer.deserialize(input, version);
1096
- states.set(address, endpointState);
1097
- }
1098
- return {states};
1099
- }
1100
- });
1101
-
1102
- export function gossipDigestAck2Handler(ctx: VerbHandlerContext): VerbHandler<GossipDigestAck2> {
1103
- return new (class extends GossipVerbHandler<GossipDigestAck2> {
1104
- doVerb(message: Message<GossipDigestAck2>) {
1105
- if (logger.enabledFor('debug')) {
1106
- const from = message.from;
1107
- logger.debug(`Received GossipDigestAck2Message with ${addressAsString(from)}`);
1108
- }
1109
- if (!this.gossiper.enabled) {
1110
- if (logger.enabledFor('debug')) {
1111
- logger.debug('Ignoring GossipDigestAck2Message because gossiper is disabled');
1112
- }
1113
- return;
1114
- }
1115
- const states = message.payload.states;
1116
- this.gossiper.notifyFailureDetector(states);
1117
- this.gossiper.applyStateLocally(states);
1118
- super.doVerb(message);
1119
- }
1120
- })(ctx);
1121
- }
1122
-
1123
-
1124
- export function echoHandler(ctx: VerbHandlerContext) {
1125
- return new class implements VerbHandler<NoPayload> {
1126
- doVerb(message: Message<NoPayload>) {
1127
- if (logger.enabledFor('trace')) {
1128
- logger.debug(`Sending ECHO_RSP to ${addressAsString(message.from)}`);
1129
- }
1130
- ctx.messaging().send(ctx.messageFactory().emptyResponse(message), message.from);
1131
- }
1132
- };
1133
- }