hamok
Version:
Lightweight Distributed Object Storage on RAFT consensus algorithm
300 lines • 12.2 kB
TypeScript
import { EventEmitter } from 'events';
import { RaftEngine, RaftEngineConfig } from './raft/RaftEngine';
import { HamokMessage } from './messages/HamokMessage';
import { LogEntry } from './raft/LogEntry';
import { RaftStateName } from './raft/RaftState';
import { HamokMap } from './collections/HamokMap';
import { HamokConnectionConfig } from './collections/HamokConnection';
import { HamokCodec } from './common/HamokCodec';
import { BaseMap } from './collections/BaseMap';
import { HamokGrid } from './HamokGrid';
import { HamokQueue } from './collections/HamokQueue';
import { HamokEmitter, HamokEmitterEventMap } from './collections/HamokEmitter';
import { RaftLogs } from './raft/RaftLogs';
import { HamokRecord, HamokRecordObject } from './collections/HamokRecord';
import { RemoteMap } from './collections/RemoteMap';
import { HamokRemoteMap } from './collections/HamokRemoteMap';
export type HamokJoinProcessParams = {
/**
* Timeout in milliseconds for fetching the remote peers.
*
* DEFAULT: 5000
*/
fetchRemotePeerTimeoutInMs?: number;
/**
* The maximum number of retries for fetching the remote peers.
* -1 - means infinite retries
* 0 - means no retries
*
* DEFAULT: 3
*/
maxRetry?: number;
};
export type HamokObjectConfig<AppData extends Record<string, unknown> = Record<string, unknown>> = {
/**
* Indicate if the Hamok should stop automatically when there are no remote peers.
*/
/**
* The timeout in milliseconds for waiting for the remote storage state.
*
* DEFAULT: 1000
*/
remoteStorageStateWaitingTimeoutInMs: number;
/**
* A custom appData object to be used by the application utilizes Hamok.
*/
appData?: AppData;
};
/**
* Configuration settings for the Hamok constructor, extending RaftEngineConfig and HamokConfig.
*/
export type HamokConfig<AppData extends Record<string, unknown> = Record<string, unknown>> = RaftEngineConfig & Partial<HamokObjectConfig<AppData>> & {
/**
* Optional. The expiration time in milliseconds for log entries.
* Log entries older than this duration may be purged.
* Only applicable when a custom RaftLogs is NOT provided.
*
* DEFAULT: 300000 (5 minutes)
*/
logEntriesExpirationTimeInMs?: number;
/**
* Optional. A map of initial log entries to be used by the system.
* The key is the log index, and the value is the LogEntry.
*/
initialLogEntries?: Map<number, LogEntry>;
/**
* The period in milliseconds to send notifications to the source(s) about ongoing requests
* to prevent timeouts. This is applicable when a requestId is explicitly added to the ongoing
* requests set by calling addOngoingRequestId(). Notifications stop when removeOngoingRequestId
* is called. It is crucial to call this explicitly in any case as comlink does not handle
* automatically stopping notifications for explicitly postponed requests.
*/
ongoingRequestsSendingPeriodInMs: number;
/**
* Optional. A custom implementation of RaftLogs to store log entries.
*/
raftLogs?: RaftLogs;
};
export type HamokConnectionBuilderBaseConfig = Pick<HamokConnectionConfig, 'requestTimeoutInMs' | 'remoteStorageStateWaitingTimeoutInMs'> & {
maxOutboundMessageKeys: number;
maxOutboundMessageValues: number;
};
/**
* Configuration settings for building a Hamok record.
*/
export type HamokRecordBuilderConfig<T extends HamokRecordObject> = Partial<HamokConnectionBuilderBaseConfig> & {
/**
* The unique identifier for the record.
*/
recordId: string;
/**
* Optional. A map of payload codecs for encoding and decoding record properties.
* The key is a property of the record, and the value is a codec for that property.
*/
payloadCodecs?: Map<keyof T, HamokCodec<T[keyof T], string>>;
/**
* Optional. An initial object to be used as the base state of the record.
*/
initialObject?: Partial<T>;
/**
* Optional. A function to determine equality between two values.
* Used for custom equality checking.
*/
equalValues?: (a: T[keyof T], b: T[keyof T]) => boolean;
};
/**
* Configuration settings for building a Hamok map.
*/
export type HamokMapBuilderConfig<K, V> = Partial<HamokConnectionBuilderBaseConfig> & {
/**
* The unique identifier for the map.
*/
mapId: string;
/**
* Optional. A codec for encoding and decoding keys in the map.
*/
keyCodec?: HamokCodec<K, Uint8Array>;
/**
* Optional. A codec for encoding and decoding values in the map.
*/
valueCodec?: HamokCodec<V, Uint8Array>;
/**
* Optional. A base map to be used as the initial state of the map.
*/
baseMap?: BaseMap<K, V>;
/**
* Optional. A function to determine equality between two values.
* Used for custom equality checking.
*/
equalValues?: (a: V, b: V) => boolean;
};
/**
* Configuration settings for building a Hamok remote map.
*/
export type HamokRemoteMapBuilderConfig<K, V> = Omit<HamokMapBuilderConfig<K, V>, 'baseMap'> & {
/**
* The remote map to be used to store the data.
*/
remoteMap: RemoteMap<K, V>;
/**
* Flag indicate if the events should be emitted by the event emitter or not.
* It also reduces the communication overhead if not needed, as for emitting events
* the leader should send a message to all followers to emit an event.
* In such case when it's not necessary (like cache maintenance) it can be disabled.
*/
noEvents?: boolean;
};
/**
* Configuration settings for building a Hamok queue.
*/
export type HamokQueueBuilderConfig<T> = Partial<HamokConnectionBuilderBaseConfig> & {
/**
* The unique identifier for the queue.
*/
queueId: string;
/**
* Optional. A codec for encoding and decoding items in the queue.
*/
codec?: HamokCodec<T, Uint8Array>;
/**
* Optional. A base map to be used as the initial state of the queue.
*/
baseMap?: BaseMap<number, T>;
};
/**
* Configuration settings for building a Hamok emitter.
*/
export type HamokEmitterBuilderConfig<T extends HamokEmitterEventMap> = Partial<HamokConnectionBuilderBaseConfig> & {
/**
* The unique identifier for the emitter.
*/
emitterId: string;
/**
* Optional. A map of payload codecs for encoding and decoding event payloads.
* The key is an event type, and the value is a codec for that event type.
*/
payloadsCodec?: Map<keyof T, {
encode: (...args: unknown[]) => string;
decode: (data: string) => unknown[];
}>;
/**
* Optional.Indicate if the emitter automatically cleans up and unssubscribes from events
* remote peers gone offline. Only leader endpoint does this.
*/
autoClean?: boolean;
};
export type HamokFetchRemotePeersResponse = {
remotePeers: string[];
minNumberOfLogs?: number;
smallestCommitIndex?: number;
};
export type HamokEventMap = {
follower: [];
candidate: [];
leader: [];
message: [message: HamokMessage];
'remote-peer-joined': [peerId: string];
'remote-peer-left': [peerId: string];
'leader-changed': [leaderId: string | undefined, prevLeader: string | undefined];
'state-changed': [newState: RaftStateName, prevState: RaftStateName];
commit: [commitIndex: number, message: HamokMessage];
heartbeat: [];
error: [error: Error];
'no-heartbeat-from': [remotePeerId: string];
'unsynced-peer': [remotePeerId: string];
joined: [];
rejoining: [];
left: [];
close: [];
};
export type HamokStorageType = 'record' | 'map' | 'remoteMap' | 'emitter' | 'queue';
export type HamokSharedStorageMap = HamokMap<string, {
type: HamokStorageType;
count: number;
}>;
export declare interface Hamok {
on<U extends keyof HamokEventMap>(event: U, listener: (...args: HamokEventMap[U]) => void): this;
once<U extends keyof HamokEventMap>(event: U, listener: (...args: HamokEventMap[U]) => void): this;
emit<U extends keyof HamokEventMap>(event: U, ...args: HamokEventMap[U]): boolean;
}
export declare class Hamok<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
readonly config: HamokObjectConfig<AppData>;
readonly raft: RaftEngine;
readonly storages: Map<string, HamokRecord<any> | HamokMap<any, any> | HamokQueue<any> | HamokRemoteMap<any, any> | HamokEmitter<any, Record<string, unknown>>>;
private _closed;
private _run;
private _raftTimer?;
private readonly _remoteHeartbeats;
private readonly _codec;
readonly grid: HamokGrid;
private _joining?;
constructor(providedConfig?: Partial<HamokConfig<AppData>>);
get appData(): AppData;
get localPeerId(): string;
get remotePeerIds(): ReadonlySet<string>;
get leader(): boolean;
get ready(): Promise<this>;
get state(): RaftStateName;
get run(): boolean;
get activeRemoteHeartbeats(): IterableIterator<string>;
get closed(): boolean;
close(): void;
get stats(): {
/**
* Number of requests sent out from the grid, but waiting for response from remote peer
*/
numberOfPendingRequests: number;
/**
* Number of requests received by this peer and queued for processing (for example requests to be waited to be committed by the leader)
*/
numberOfOngoingRequests: number;
/**
* Number of responses received by this peer and queued for processing as the response were chunked
*/
numberOfPendingResponses: number;
/**
* Number of remote peers this peer is connected to
*/
numberOfRemotePeers: number;
/**
* Number of bytes used by the raft logs in memory
*/
raftLogsBytesInMemory: number;
};
private _acceptCommit;
addRemotePeerId(remoteEndpointId: string): void;
removeRemotePeerId(remotePeerId: string): void;
private _acceptLeaderChanged;
/**
* Wait until the commit head (the most recent spread commit by the leader) is reached
* @returns
*/
waitUntilCommitHead(): Promise<void>;
waitUntilLeader(timeoutInMs?: number): Promise<void>;
createMap<K, V>(options: HamokMapBuilderConfig<K, V>): HamokMap<K, V>;
getOrCreateMap<K, V>(options: HamokMapBuilderConfig<K, V>, callback?: (alreadyExisted: boolean) => void): HamokMap<K, V>;
createRemoteMap<K, V>(options: HamokRemoteMapBuilderConfig<K, V>): HamokRemoteMap<K, V>;
getOrCreateRemoteMap(options: HamokRemoteMapBuilderConfig<unknown, unknown>, callback?: (alreadyExisted: boolean) => void): HamokRemoteMap<unknown, unknown>;
createRecord<T extends HamokRecordObject>(options: HamokRecordBuilderConfig<T>): HamokRecord<T>;
getOrCreateRecord<T extends HamokRecordObject>(options: HamokRecordBuilderConfig<T>, callback?: (alreadyExisted: boolean) => void): HamokRecord<T>;
createQueue<T>(options: HamokQueueBuilderConfig<T>): HamokQueue<T>;
getOrCreateQueue<T>(options: HamokQueueBuilderConfig<T>, callback?: (alreadyExisted: boolean) => void): HamokQueue<T>;
createEmitter<T extends HamokEmitterEventMap, M extends Record<string, unknown> = Record<string, unknown>>(options: HamokEmitterBuilderConfig<T>): HamokEmitter<T, M>;
getOrCreateEmitter<T extends HamokEmitterEventMap>(options: HamokEmitterBuilderConfig<T>, callback?: (alreadyExisted: boolean) => void): HamokEmitter<T>;
submit(entry: HamokMessage): Promise<void>;
accept(message: HamokMessage, commitIndex?: number): void;
leave(): Promise<void>;
join(params?: HamokJoinProcessParams): Promise<void>;
private _join;
private _startRaftEngine;
private _stopRaftEngine;
private _sendEndpointNotification;
private _acceptGridMessage;
broadcastEndpointNotification(): void;
private _emitMessage;
private _createStorageConnection;
private _acceptKeepAliveHamokMessage;
private _checkRemoteHeartbeats;
private _sendJoinMsg;
}
//# sourceMappingURL=Hamok.d.ts.map