UNPKG

hamok

Version:

Lightweight Distributed Object Storage on RAFT consensus algorithm

975 lines (841 loc) 34 kB
import { HamokCodec, encodeMap, decodeMap, encodeSet, decodeSet, createStrToUint8ArrayCodec } from '../common/HamokCodec'; import { EntryUpdatedNotification, UpdateEntriesNotification, UpdateEntriesRequest, UpdateEntriesResponse } from './messagetypes/UpdateEntries'; import { HamokMessage as Message, HamokMessage_MessageType as MessageType } from './HamokMessage'; import { ClearEntriesNotification, ClearEntriesRequest, ClearEntriesResponse } from './messagetypes/ClearEntries'; import { GetEntriesRequest, GetEntriesResponse } from './messagetypes/GetEntries'; import { GetKeysRequest, GetKeysResponse } from './messagetypes/GetKeys'; import { DeleteEntriesNotification, DeleteEntriesRequest, DeleteEntriesResponse } from './messagetypes/DeleteEntries'; import { EntriesRemovedNotification, RemoveEntriesNotification, RemoveEntriesRequest, RemoveEntriesResponse } from './messagetypes/RemoveEntries'; import { EntriesInsertedNotification, InsertEntriesNotification, InsertEntriesRequest, InsertEntriesResponse } from './messagetypes/InsertEntries'; import { GetSizeRequest, GetSizeResponse } from './messagetypes/GetSize'; import { createLogger } from '../common/logger'; import { StorageAppliedCommitNotification } from './messagetypes/StorageAppliedCommit'; import { StorageHelloNotification } from './messagetypes/StorageHelloNotification'; import { StorageStateNotification } from './messagetypes/StorageStateNotification'; const logger = createLogger('StorageCodec'); type Input<K, V> = ClearEntriesNotification | ClearEntriesRequest | ClearEntriesResponse | GetEntriesRequest<K> | GetEntriesResponse<K, V> | GetKeysRequest | GetKeysResponse<K> | GetSizeRequest | GetSizeResponse | DeleteEntriesNotification<K> | DeleteEntriesRequest<K> | DeleteEntriesResponse<K> | RemoveEntriesNotification<K> | RemoveEntriesRequest<K> | RemoveEntriesResponse<K, V> | EntriesRemovedNotification<K, V> | InsertEntriesNotification<K, V> | InsertEntriesRequest<K, V> | InsertEntriesResponse<K, V> | EntriesInsertedNotification<K, V> | UpdateEntriesNotification<K, V> | UpdateEntriesRequest<K, V> | UpdateEntriesResponse<K, V> | EntryUpdatedNotification<K, V> | StorageAppliedCommitNotification | StorageHelloNotification | StorageStateNotification ; export type StorageCodecMessageMap<K, V> = { ClearEntriesRequest: ClearEntriesRequest; ClearEntriesResponse: ClearEntriesResponse; ClearEntriesNotification: ClearEntriesNotification; GetEntriesRequest: GetEntriesRequest<K>; GetEntriesResponse: GetEntriesResponse<K, V>; GetKeysRequest: GetKeysRequest; GetKeysResponse: GetKeysResponse<K>; GetSizeRequest: GetSizeRequest; GetSizeResponse: GetSizeResponse; DeleteEntriesRequest: DeleteEntriesRequest<K>; DeleteEntriesResponse: DeleteEntriesResponse<K>; DeleteEntriesNotification: DeleteEntriesNotification<K>; RemoveEntriesRequest: RemoveEntriesRequest<K>; RemoveEntriesResponse: RemoveEntriesResponse<K, V>; RemoveEntriesNotification: RemoveEntriesNotification<K>; EntriesRemovedNotification: EntriesRemovedNotification<K, V>; InsertEntriesRequest: InsertEntriesRequest<K, V>; InsertEntriesResponse: InsertEntriesResponse<K, V>; InsertEntriesNotification: InsertEntriesNotification<K, V>; EntriesInsertedNotification: EntriesInsertedNotification<K, V>; UpdateEntriesRequest: UpdateEntriesRequest<K, V>; UpdateEntriesResponse: UpdateEntriesResponse<K, V>; UpdateEntriesNotification: UpdateEntriesNotification<K, V>; EntryUpdatedNotification: EntryUpdatedNotification<K, V>; StorageAppliedCommitNotification: StorageAppliedCommitNotification; StorageHelloNotification: StorageHelloNotification; StorageStateNotification: StorageStateNotification; } export class StorageCodec<K, V> implements HamokCodec<Input<K, V>, Message> { private static readonly strCodec = createStrToUint8ArrayCodec(); public constructor( public readonly keyCodec: HamokCodec<K, Uint8Array>, public readonly valueCodec: HamokCodec<V, Uint8Array>, ) { // empty } encode(input: Input<K, V>, callback?: <U extends keyof StorageCodecMessageMap<K, V>>(type: U, message: Message) => void): Message { let result: Message; switch (input.constructor) { case ClearEntriesRequest: result = this.encodeClearEntriesRequest(input as ClearEntriesRequest); break; case ClearEntriesResponse: result = this.encodeClearEntriesResponse(input as ClearEntriesResponse); break; case ClearEntriesNotification: result = this.encodeClearEntriesNotification(input as ClearEntriesNotification); break; case GetEntriesRequest: result = this.encodeGetEntriesRequest(input as GetEntriesRequest<K>); break; case GetEntriesResponse: result = this.encodeGetEntriesResponse(input as GetEntriesResponse<K, V>); break; case GetKeysRequest: result = this.encodeGetKeysRequest(input as GetKeysRequest); break; case GetKeysResponse: result = this.encodeGetKeysResponse(input as GetKeysResponse<K>); break; case GetSizeRequest: result = this.encodeGetSizeRequest(input as GetSizeRequest); break; case GetSizeResponse: result = this.encodeGetSizeResponse(input as GetSizeResponse); break; case DeleteEntriesRequest: result = this.encodeDeleteEntriesRequest(input as DeleteEntriesRequest<K>); break; case DeleteEntriesResponse: result = this.encodeDeleteEntriesResponse(input as DeleteEntriesResponse<K>); break; case DeleteEntriesNotification: result = this.encodeDeleteEntriesNotification(input as DeleteEntriesNotification<K>); break; case RemoveEntriesRequest: result = this.encodeRemoveEntriesRequest(input as RemoveEntriesRequest<K>); break; case RemoveEntriesResponse: result = this.encodeRemoveEntriesResponse(input as RemoveEntriesResponse<K, V>); break; case RemoveEntriesNotification: result = this.encodeDeleteEntriesNotification(input as RemoveEntriesNotification<K>); break; case EntriesRemovedNotification: result = this.encodeEntriesRemovedNotification(input as EntriesRemovedNotification<K, V>); break; case InsertEntriesRequest: result = this.encodeInsertEntriesRequest(input as InsertEntriesRequest<K, V>); break; case InsertEntriesResponse: result = this.encodeInsertEntriesResponse(input as InsertEntriesResponse<K, V>); break; case InsertEntriesNotification: result = this.encodeInsertEntriesNotification(input as InsertEntriesNotification<K, V>); break; case EntriesInsertedNotification: result = this.encodeEntriesInsertedNotification(input as EntriesInsertedNotification<K, V>); break; case UpdateEntriesRequest: result = this.encodeUpdateEntriesRequest(input as UpdateEntriesRequest<K, V>); break; case UpdateEntriesResponse: result = this.encodeUpdateEntriesResponse(input as UpdateEntriesResponse<K, V>); break; case UpdateEntriesNotification: result = this.encodeUpdateEntriesNotification(input as UpdateEntriesNotification<K, V>); break; case EntryUpdatedNotification: result = this.encodeEntryUpdatedNotification(input as EntryUpdatedNotification<K, V>); break; case StorageAppliedCommitNotification: result = this.encodeStorageAppliedCommitNotification(input as StorageAppliedCommitNotification); break; case StorageHelloNotification: result = this.encodeStorageHelloNotification(input as StorageHelloNotification); break; case StorageStateNotification: result = this.encodeStorageStateNotification(input as StorageStateNotification); break; default: throw new Error(`Cannot encode input${ input}`); } logger.trace('Encoded message %o', input); if (callback) { callback(input.constructor.name as keyof StorageCodecMessageMap<K, V>, result); } return result; } decode(message: Message, callback?: <U extends keyof StorageCodecMessageMap<K, V>>(type: U, output: StorageCodecMessageMap<K, V>[U]) => void): Input<K, V> { let type: keyof StorageCodecMessageMap<K, V> | undefined; let result: Input<K, V> | undefined; switch (message.type) { case MessageType.CLEAR_ENTRIES_REQUEST: type = callback ? 'ClearEntriesRequest' : undefined; result = this.decodeClearEntriesRequest(message); break; case MessageType.CLEAR_ENTRIES_RESPONSE: type = callback ? 'ClearEntriesResponse' : undefined; result = this.decodeClearEntriesResponse(message); break; case MessageType.CLEAR_ENTRIES_NOTIFICATION: type = callback ? 'ClearEntriesNotification' : undefined; result = this.decodeClearEntriesNotification(message); break; case MessageType.GET_ENTRIES_REQUEST: type = callback ? 'GetEntriesRequest' : undefined; result = this.decodeGetEntriesRequest(message); break; case MessageType.GET_ENTRIES_RESPONSE: type = callback ? 'GetEntriesResponse' : undefined; result = this.decodeGetEntriesResponse(message); break; case MessageType.GET_SIZE_REQUEST: type = callback ? 'GetSizeRequest' : undefined; result = this.decodeGetSizeRequest(message); break; case MessageType.GET_SIZE_RESPONSE: type = callback ? 'GetSizeResponse' : undefined; result = this.decodeGetSizeResponse(message); break; case MessageType.GET_KEYS_REQUEST: type = callback ? 'GetKeysRequest' : undefined; result = this.decodeGetKeysRequest(message); break; case MessageType.GET_KEYS_RESPONSE: type = callback ? 'GetKeysResponse' : undefined; result = this.decodeGetKeysResponse(message); break; case MessageType.DELETE_ENTRIES_REQUEST: type = callback ? 'DeleteEntriesRequest' : undefined; result = this.decodeDeleteEntriesRequest(message); break; case MessageType.DELETE_ENTRIES_RESPONSE: type = callback ? 'DeleteEntriesResponse' : undefined; result = this.decodeDeleteEntriesResponse(message); break; case MessageType.DELETE_ENTRIES_NOTIFICATION: type = callback ? 'DeleteEntriesNotification' : undefined; result = this.decodeDeleteEntriesNotification(message); break; case MessageType.REMOVE_ENTRIES_REQUEST: type = callback ? 'RemoveEntriesRequest' : undefined; result = this.decodeRemoveEntriesRequest(message); break; case MessageType.REMOVE_ENTRIES_RESPONSE: type = callback ? 'RemoveEntriesResponse' : undefined; result = this.decodeRemoveEntriesResponse(message); break; case MessageType.REMOVE_ENTRIES_NOTIFICATION: type = callback ? 'RemoveEntriesNotification' : undefined; result = this.decodeRemoveEntriesNotification(message); break; case MessageType.ENTRIES_REMOVED_NOTIFICATION: type = callback ? 'EntriesRemovedNotification' : undefined; result = this.decodeEntriesRemovedNotification(message); break; case MessageType.INSERT_ENTRIES_REQUEST: type = callback ? 'InsertEntriesRequest' : undefined; result = this.decodeInsertEntriesRequest(message); break; case MessageType.INSERT_ENTRIES_RESPONSE: type = callback ? 'InsertEntriesResponse' : undefined; result = this.decodeInsertEntriesResponse(message); break; case MessageType.INSERT_ENTRIES_NOTIFICATION: type = callback ? 'InsertEntriesNotification' : undefined; result = this.decodeInsertEntriesNotification(message); break; case MessageType.ENTRIES_INSERTED_NOTIFICATION: type = callback ? 'EntriesInsertedNotification' : undefined; result = this.decodeEntriesInsertedNotification(message); break; case MessageType.UPDATE_ENTRIES_REQUEST: type = callback ? 'UpdateEntriesRequest' : undefined; result = this.decodeUpdateEntriesRequest(message); break; case MessageType.UPDATE_ENTRIES_RESPONSE: type = callback ? 'UpdateEntriesResponse' : undefined; result = this.decodeUpdateEntriesResponse(message); break; case MessageType.UPDATE_ENTRIES_NOTIFICATION: type = callback ? 'UpdateEntriesNotification' : undefined; result = this.decodeUpdateEntriesNotification(message); break; case MessageType.ENTRY_UPDATED_NOTIFICATION: type = callback ? 'EntryUpdatedNotification' : undefined; result = this.decodeEntryUpdatedNotification(message); break; case MessageType.STORAGE_APPLIED_COMMIT_NOTIFICATION: type = callback ? 'StorageAppliedCommitNotification' : undefined; result = this.decodeStorageAppliedCommitNotification(message); break; case MessageType.STORAGE_HELLO_NOTIFICATION: type = callback ? 'StorageHelloNotification' : undefined; result = this.decodeStorageHelloNotification(message); break; case MessageType.STORAGE_STATE_NOTIFICATION: type = callback ? 'StorageStateNotification' : undefined; result = this.decodeStorageStateNotification(message); break; } logger.trace('Decoded message %o', message); if (!result) { throw new Error(`Cannot decode message ${message.type}`); } if (type && callback) { callback(type, result); } return result; } public encodeStorageHelloNotification(notification: StorageHelloNotification): Message { return new Message({ type: MessageType.STORAGE_HELLO_NOTIFICATION, sourceId: notification.sourceEndpointId, }); } public decodeStorageHelloNotification(message: Message): StorageHelloNotification { if (message.type !== MessageType.STORAGE_HELLO_NOTIFICATION) { throw new Error('decodeStorageCreatedNotification(): Message type must be STORAGE_HELLO_NOTIFICATION'); } return new StorageHelloNotification( message.sourceId!, ); } public encodeStorageStateNotification(storageState: StorageStateNotification): Message { return new Message({ type: MessageType.STORAGE_STATE_NOTIFICATION, sourceId: storageState.sourceEndpointId, snapshot: storageState.serializedStorageSnapshot ? StorageCodec.strCodec.encode(storageState.serializedStorageSnapshot) : undefined, raftCommitIndex: storageState.commitIndex, }); } public decodeStorageStateNotification(message: Message): StorageStateNotification { if (message.type !== MessageType.STORAGE_STATE_NOTIFICATION) { throw new Error('decodeStorageSnapshot(): Message type must be STORAGE_STATE_NOTIFICATION'); } return new StorageStateNotification( message.sourceId!, message.raftCommitIndex!, message.snapshot ? StorageCodec.strCodec.decode(message.snapshot) : undefined, ); } public encodeClearEntriesRequest(request: ClearEntriesRequest): Message { return new Message({ type: MessageType.CLEAR_ENTRIES_REQUEST, requestId: request.requestId, sourceId: request.sourceEndpointId, }); } public decodeClearEntriesRequest(message: Message): ClearEntriesRequest { if (message.type !== MessageType.CLEAR_ENTRIES_REQUEST) { throw new Error('decodeClearEntriesRequest(): Message type must be CLEAR_ENTRIES_REQUEST'); } return new ClearEntriesRequest( message.requestId!, message.sourceId, ); } public encodeClearEntriesResponse(response: ClearEntriesResponse): Message { return new Message({ type: MessageType.CLEAR_ENTRIES_RESPONSE, requestId: response.requestId, destinationId: response.destinationEndpointId, }); } public decodeClearEntriesResponse(message: Message): ClearEntriesResponse { if (message.type !== MessageType.CLEAR_ENTRIES_RESPONSE) { throw new Error('decodeClearEntriesResponse(): Message type must be CLEAR_ENTRIES_RESPONSE'); } return new ClearEntriesResponse( message.requestId!, message.destinationId, ); } public encodeClearEntriesNotification(notification: ClearEntriesNotification): Message { return new Message({ type: MessageType.CLEAR_ENTRIES_NOTIFICATION, sourceId: notification.sourceEndpointId, destinationId: notification.destinationEndpointId, }); } public decodeClearEntriesNotification(message: Message): ClearEntriesNotification { if (message.type !== MessageType.CLEAR_ENTRIES_NOTIFICATION) { throw new Error('decodeClearEntriesNotification(): Message type must be CLEAR_ENTRIES_NOTIFICATION'); } return new ClearEntriesNotification( message.sourceId, message.destinationId, ); } public encodeGetEntriesRequest(request: GetEntriesRequest<K>): Message { const keys = this.encodeKeys(request.keys); return new Message({ type: MessageType.GET_ENTRIES_REQUEST, requestId: request.requestId, keys, sourceId: request.sourceEndpointId, }); } public decodeGetEntriesRequest(message: Message): GetEntriesRequest<K> { if (message.type !== MessageType.GET_ENTRIES_REQUEST) { throw new Error('decodeGetEntriesRequest(): Message type must be GET_ENTRIES_REQUEST'); } const keys = this.decodeKeys(message.keys); return new GetEntriesRequest<K>( keys, message.requestId!, message.sourceId, ); } public encodeGetEntriesResponse(response: GetEntriesResponse<K, V>): Message { const [ keys, values ] = this.encodeEntries(response.foundEntries); return new Message({ type: MessageType.GET_ENTRIES_RESPONSE, requestId: response.requestId, keys, values, destinationId: response.destinationEndpointId, }); } public decodeGetEntriesResponse(message: Message): GetEntriesResponse<K, V> { if (message.type !== MessageType.GET_ENTRIES_RESPONSE) { throw new Error('decodeGetEntriesResponse(): Message type must be GET_ENTRIES_RESPONSE'); } const foundEntries = this.decodeEntries(message.keys, message.values); return new GetEntriesResponse<K, V>( message.requestId!, foundEntries, message.destinationId, ); } public encodeGetSizeRequest(request: GetSizeRequest): Message { return new Message({ type: MessageType.GET_SIZE_REQUEST, requestId: request.requestId, sourceId: request.sourceEndpointId }); } public decodeGetSizeRequest(message: Message): GetSizeRequest { if (message.type !== MessageType.GET_SIZE_REQUEST) { throw new Error('decodeGetSizeRequest(): Message type must be GET_SIZE_REQUEST'); } return new GetSizeRequest( message.requestId!, message.sourceId, ); } public encodeGetSizeResponse(response: GetSizeResponse): Message { return new Message({ type: MessageType.GET_SIZE_RESPONSE, requestId: response.requestId, storageSize: response.size, destinationId: response.destinationEndpointId, }); } public decodeGetSizeResponse(message: Message): GetSizeResponse { if (message.type !== MessageType.GET_SIZE_RESPONSE) { throw new Error('decodeGetSizeResponse(): Message type must be GET_SIZE_RESPONSE'); } return new GetSizeResponse( message.requestId!, message.storageSize!, message.destinationId, ); } public encodeGetKeysRequest(request: GetKeysRequest): Message { return new Message({ type: MessageType.GET_KEYS_REQUEST, requestId: request.requestId, sourceId: request.sourceEndpointId }); } public decodeGetKeysRequest(message: Message): GetKeysRequest { if (message.type !== MessageType.GET_KEYS_REQUEST) { throw new Error('decodeGetKeysRequest(): Message type must be GET_KEYS_REQUEST'); } return new GetKeysRequest( message.requestId!, message.sourceId, ); } public encodeGetKeysResponse(response: GetKeysResponse<K>): Message { const keys = this.encodeKeys(response.keys); return new Message({ type: MessageType.GET_KEYS_RESPONSE, requestId: response.requestId, destinationId: response.destinationEndpointId, keys }); } public decodeGetKeysResponse(message: Message): GetKeysResponse<K> { if (message.type !== MessageType.GET_KEYS_RESPONSE) { throw new Error('decodeGetKeysResponse(): Message type must be GET_ENTRIES_RESPONSE'); } const keys = this.decodeKeys(message.keys); return new GetKeysResponse<K>( message.requestId!, keys, message.destinationId, ); } public encodeDeleteEntriesRequest(request: DeleteEntriesRequest<K>): Message { const keys = this.encodeKeys(request.keys); return new Message({ type: MessageType.DELETE_ENTRIES_REQUEST, requestId: request.requestId, keys, sourceId: request.sourceEndpointId }); } public decodeDeleteEntriesRequest(message: Message): DeleteEntriesRequest<K> { if (message.type !== MessageType.DELETE_ENTRIES_REQUEST) { throw new Error('decodeDeleteEntriesRequest(): Message type must be DELETE_ENTRIES_REQUEST'); } const keys = this.decodeKeys(message.keys); return new DeleteEntriesRequest<K>( message.requestId!, keys, message.sourceId, ); } public encodeDeleteEntriesResponse(response: DeleteEntriesResponse<K>): Message { const keys = this.encodeKeys(response.deletedKeys); return new Message({ type: MessageType.DELETE_ENTRIES_RESPONSE, requestId: response.requestId, keys, destinationId: response.destinationEndpointId }); } public decodeDeleteEntriesResponse(message: Message): DeleteEntriesResponse<K> { if (message.type !== MessageType.DELETE_ENTRIES_RESPONSE) { throw new Error('decodeDeleteEntriesResponse(): Message type must be DELETE_ENTRIES_RESPONSE'); } const deletedKeys = this.decodeKeys(message.keys); return new DeleteEntriesResponse<K>( message.requestId!, deletedKeys, message.destinationId, ); } public encodeDeleteEntriesNotification(notification: DeleteEntriesNotification<K>): Message { const keys = this.encodeKeys(notification.keys); return new Message({ type: MessageType.DELETE_ENTRIES_NOTIFICATION, keys, sourceId: notification.sourceEndpointId, destinationId: notification.destinationEndpointId }); } public decodeDeleteEntriesNotification(message: Message): DeleteEntriesNotification<K> { if (message.type !== MessageType.DELETE_ENTRIES_NOTIFICATION) { throw new Error('decodeDeleteNotification(): Message type must be DELETE_ENTRIES_NOTIFICATION'); } const keys = this.decodeKeys(message.keys); return new DeleteEntriesNotification<K>( keys, message.sourceId, message.destinationId, ); } public encodeRemoveEntriesRequest(request: RemoveEntriesRequest<K>): Message { const keys = this.encodeKeys(request.keys); return new Message({ type: MessageType.REMOVE_ENTRIES_REQUEST, requestId: request.requestId, keys, sourceId: request.sourceEndpointId, prevValue: request.prevValue !== undefined ? this.valueCodec.encode(request.prevValue as V) : undefined, }); } public decodeRemoveEntriesRequest(message: Message): RemoveEntriesRequest<K> { if (message.type !== MessageType.REMOVE_ENTRIES_REQUEST) { throw new Error('decodeRemoveRequest(): Message type must be REMOVE_ENTRIES_REQUEST'); } const keys = this.decodeKeys(message.keys); return new RemoveEntriesRequest<K>( message.requestId!, keys, message.prevValue !== undefined ? this.valueCodec.decode(message.prevValue) : undefined, message.sourceId, ); } public encodeRemoveEntriesResponse(response: RemoveEntriesResponse<K, V>): Message { const [ keys, values ] = this.encodeEntries(response.removedEntries); return new Message({ type: MessageType.REMOVE_ENTRIES_RESPONSE, requestId: response.requestId, keys, values, destinationId: response.destinationEndpointId }); } public decodeRemoveEntriesResponse(message: Message): RemoveEntriesResponse<K, V> { if (message.type !== MessageType.REMOVE_ENTRIES_RESPONSE) { throw new Error('decodeRemoveResponse(): Message type must be REMOVE_ENTRIES_RESPONSE'); } const removedEntries = this.decodeEntries(message.keys, message.values); return new RemoveEntriesResponse<K, V>( message.requestId!, removedEntries, message.destinationId, ); } public encodeRemoveEntriesNotification(notification: RemoveEntriesNotification<K>): Message { const keys = this.encodeKeys(notification.keys); return new Message({ type: MessageType.REMOVE_ENTRIES_NOTIFICATION, keys, sourceId: notification.sourceEndpointId, destinationId: notification.destinationEndpointId }); } public decodeRemoveEntriesNotification(message: Message): RemoveEntriesNotification<K> { if (message.type !== MessageType.REMOVE_ENTRIES_NOTIFICATION) { throw new Error('decodeRemoveNotification(): Message type must be REMOVE_ENTRIES_NOTIFICATION'); } const keys = this.decodeKeys(message.keys); return new RemoveEntriesNotification<K>( keys, message.sourceId, message.destinationId, ); } public encodeEntriesRemovedNotification(notification: EntriesRemovedNotification<K, V>): Message { const [ keys, values ] = this.encodeEntries(notification.entries); return new Message({ type: MessageType.ENTRIES_REMOVED_NOTIFICATION, keys, values, sourceId: notification.sourceEndpointId, destinationId: notification.destinationEndpointId, }); } public decodeEntriesRemovedNotification(message: Message): EntriesRemovedNotification<K, V> { if (message.type !== MessageType.ENTRIES_REMOVED_NOTIFICATION) { throw new Error('decodeEntriesRemovedNotification(): Message type must be ENTRIES_REMOVED_NOTIFICATION'); } const entries = this.decodeEntries(message.keys, message.values); return new EntriesRemovedNotification<K, V>( entries, message.sourceId, message.destinationId, ); } public encodeInsertEntriesRequest(request: InsertEntriesRequest<K, V>): Message { const [ keys, values ] = this.encodeEntries(request.entries); return new Message({ type: MessageType.INSERT_ENTRIES_REQUEST, requestId: request.requestId, keys, values, sourceId: request.sourceEndpointId }); } public decodeInsertEntriesRequest(message: Message): InsertEntriesRequest<K, V> { if (message.type !== MessageType.INSERT_ENTRIES_REQUEST) { throw new Error('decodeInsertRequest(): Message type must be INSERT_ENTRIES_REQUEST'); } const entries = this.decodeEntries(message.keys, message.values); return new InsertEntriesRequest<K, V>( message.requestId!, entries, message.sourceId, ); } public encodeInsertEntriesResponse(response: InsertEntriesResponse<K, V>): Message { const [ keys, values ] = this.encodeEntries(response.existingEntries); return new Message({ type: MessageType.INSERT_ENTRIES_RESPONSE, requestId: response.requestId, keys, values, destinationId: response.destinationEndpointId }); } public decodeInsertEntriesResponse(message: Message): InsertEntriesResponse<K, V> { if (message.type !== MessageType.INSERT_ENTRIES_RESPONSE) { throw new Error('decodeInsertResponse(): Message type must be INSERT_ENTRIES_RESPONSE'); } const entries = this.decodeEntries(message.keys, message.values); return new InsertEntriesResponse<K, V>( message.requestId!, entries, message.destinationId, ); } public encodeInsertEntriesNotification(notification: InsertEntriesNotification<K, V>): Message { const [ keys, values ] = this.encodeEntries(notification.entries); return new Message({ type: MessageType.INSERT_ENTRIES_NOTIFICATION, sourceId: notification.sourceEndpointId, keys, values, destinationId: notification.destinationEndpointId }); } public decodeInsertEntriesNotification(message: Message): InsertEntriesNotification<K, V> { if (message.type !== MessageType.INSERT_ENTRIES_NOTIFICATION) { throw new Error('decodeInsertNotification(): Message type must be INSERT_ENTRIES_NOTIFICATION'); } const entries = this.decodeEntries(message.keys, message.values); return new InsertEntriesNotification<K, V>( entries, message.sourceId, message.destinationId ); } public encodeEntriesInsertedNotification(notification: EntriesInsertedNotification<K, V>): Message { const [ keys, values ] = this.encodeEntries(notification.entries); return new Message({ type: MessageType.ENTRIES_INSERTED_NOTIFICATION, keys, values, sourceId: notification.sourceEndpointId, destinationId: notification.destinationEndpointId, }); } public decodeEntriesInsertedNotification(message: Message): EntriesInsertedNotification<K, V> { if (message.type !== MessageType.ENTRIES_INSERTED_NOTIFICATION) { throw new Error('decodeEntriesInsertedNotification(): Message type must be ENTRIES_INSERTED_NOTIFICATION'); } const entries = this.decodeEntries(message.keys, message.values); return new EntriesInsertedNotification<K, V>( entries, message.sourceId, message.destinationId, ); } public encodeUpdateEntriesRequest(request: UpdateEntriesRequest<K, V>): Message { const [ keys, values ] = this.encodeEntries(request.entries); return new Message({ type: MessageType.UPDATE_ENTRIES_REQUEST, sourceId: request.sourceEndpointId, requestId: request.requestId, keys, values, // prevValue: request.prevValue !== undefined ? this.valueCodec.encode(request.prevValue) : undefined, prevValue: request.prevValue !== undefined ? this.valueCodec.encode(request.prevValue) : undefined, }); } public decodeUpdateEntriesRequest(message: Message): UpdateEntriesRequest<K, V> { if (message.type !== MessageType.UPDATE_ENTRIES_REQUEST) { throw new Error('decodeUpdateEntriesRequest(): Message type must be UPDATE_ENTRIES_REQUEST'); } const entries = this.decodeEntries(message.keys, message.values); return new UpdateEntriesRequest<K, V>( message.requestId!, entries, message.sourceId, message.prevValue !== undefined ? this.valueCodec.decode(message.prevValue) : undefined, ); } public encodeUpdateEntriesResponse(response: UpdateEntriesResponse<K, V>): Message { const [ keys, values ] = this.encodeEntries(response.updatedEntries); return new Message({ type: MessageType.UPDATE_ENTRIES_RESPONSE, requestId: response.requestId, keys, values, destinationId: response.destinationEndpointId, }); } public decodeUpdateEntriesResponse(message: Message): UpdateEntriesResponse<K, V> { if (message.type !== MessageType.UPDATE_ENTRIES_RESPONSE) { throw new Error('decodeUpdateEntriesResponse(): Message type must be UPDATE_ENTRIES_RESPONSE'); } const updatedEntries = this.decodeEntries(message.keys, message.values); return new UpdateEntriesResponse<K, V>( message.requestId!, updatedEntries, message.destinationId, ); } public encodeUpdateEntriesNotification(notification: UpdateEntriesNotification<K, V>): Message { const [ keys, values ] = this.encodeEntries(notification.updatedEntries); return new Message({ type: MessageType.UPDATE_ENTRIES_NOTIFICATION, keys, values, sourceId: notification.sourceEndpointId, destinationId: notification.destinationEndpointId, }); } public decodeUpdateEntriesNotification(message: Message): UpdateEntriesNotification<K, V> { if (message.type !== MessageType.UPDATE_ENTRIES_NOTIFICATION) { throw new Error('decodeUpdateEntriesResponse(): Message type must be UPDATE_ENTRIES_RESPONSE'); } const updatedEntries = this.decodeEntries(message.keys, message.values); return new UpdateEntriesNotification<K, V>( updatedEntries, message.sourceId, message.destinationId, ); } public encodeEntryUpdatedNotification(notification: EntryUpdatedNotification<K, V>): Message { return new Message({ type: MessageType.ENTRY_UPDATED_NOTIFICATION, keys: [ this.keyCodec.encode(notification.key) ], values: [ this.valueCodec.encode(notification.newValue) ], prevValue: this.valueCodec.encode(notification.oldValue), sourceId: notification.sourceEndpointId, destinationId: notification.destinationEndpointId, }); } public decodeEntryUpdatedNotification(message: Message): EntryUpdatedNotification<K, V> { if (message.type !== MessageType.ENTRY_UPDATED_NOTIFICATION) { throw new Error('decodeEntriesUpdatedNotification(): Message type must be ENTRY_UPDATED_NOTIFICATION'); } else if (message.keys.length < 1) { throw new Error('decodeEntriesUpdatedNotification(): Message must have at least one key'); } else if (message.values.length < 1) { throw new Error('decodeEntriesUpdatedNotification(): Message must have at least one value'); } else if (message.prevValue === undefined) { throw new Error('decodeEntriesUpdatedNotification(): Message must have a prevValue'); } const key = this.keyCodec.decode(message.keys[0]); const newValue = this.valueCodec.decode(message.values[0]); const oldValue = this.valueCodec.decode(message.prevValue!); return new EntryUpdatedNotification<K, V>( key, newValue, oldValue, message.sourceId, message.destinationId, ); } public encodeStorageAppliedCommitNotification(notification: StorageAppliedCommitNotification): Message { return new Message({ type: MessageType.STORAGE_APPLIED_COMMIT_NOTIFICATION, raftCommitIndex: notification.appliedCommitIndex, sourceId: notification.sourceEndpointId, }); } public decodeStorageAppliedCommitNotification(message: Message): StorageAppliedCommitNotification { if (message.type !== MessageType.STORAGE_APPLIED_COMMIT_NOTIFICATION) { throw new Error('decodeStorageAppliedCommitNotification(): Message type must be STORAGE_APPLIED_COMMIT_NOTIFICATION'); } return new StorageAppliedCommitNotification( message.raftCommitIndex!, message.sourceId, ); } public encodeKeys(keys: ReadonlySet<K>): Uint8Array[] { return encodeSet<K>(keys, this.keyCodec); } public decodeKeys(keys: Uint8Array[]): ReadonlySet<K> { return decodeSet<K>(keys, this.keyCodec); } public encodeEntries(entries: ReadonlyMap<K, V>): [keys: Uint8Array[], values: Uint8Array[]] { return encodeMap<K, V>(entries, this.keyCodec, this.valueCodec); } public decodeEntries(keys: Uint8Array[], values: Uint8Array[]): ReadonlyMap<K, V> { return decodeMap<K, V>(keys, values, this.keyCodec, this.valueCodec); } }