hamok
Version:
Lightweight Distributed Object Storage on RAFT consensus algorithm
157 lines (156 loc) • 6.79 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HamokGrid = void 0;
const logger_1 = require("./common/logger");
const PendingRequest_1 = require("./messages/PendingRequest");
const PendingResponse_1 = require("./messages/PendingResponse");
const logger = (0, logger_1.createLogger)('HamokGrid');
class HamokGrid {
sendMessage;
submit;
waitUntilCommitHead;
ongoingRequestsNotifier;
remotePeerIds;
logs;
_getLocalPeerId;
_getLeaderId;
pendingRequests = new Map();
pendingResponses = new Map();
constructor(sendMessage, submit, waitUntilCommitHead, ongoingRequestsNotifier, remotePeerIds, logs, _getLocalPeerId, _getLeaderId) {
this.sendMessage = sendMessage;
this.submit = submit;
this.waitUntilCommitHead = waitUntilCommitHead;
this.ongoingRequestsNotifier = ongoingRequestsNotifier;
this.remotePeerIds = remotePeerIds;
this.logs = logs;
this._getLocalPeerId = _getLocalPeerId;
this._getLeaderId = _getLeaderId;
// empty
}
get localPeerId() {
return this._getLocalPeerId();
}
get leaderId() {
return this._getLeaderId();
}
get connected() {
return this.leaderId !== undefined;
}
async request(options) {
const requestId = options.message.requestId;
const storageId = options.message.storageId;
if (!requestId) {
logger.warn('Cannot send request message without a requestId %o', options.message);
return Promise.resolve([]);
}
const remotePeers = options.targetPeerIds
? typeof options.targetPeerIds === 'string' ? new Set([options.targetPeerIds])
: Array.isArray(options.targetPeerIds) ? new Set(options.targetPeerIds) : options.targetPeerIds
: undefined;
const pendingRequest = new PendingRequest_1.PendingRequest({
requestId,
neededResponses: options.neededResponses ?? 0,
remotePeers,
timeoutInMs: options.timeoutInMs ?? 0,
storageId,
});
const prevPendingRequest = this.pendingRequests.get(pendingRequest.id);
if (prevPendingRequest) {
prevPendingRequest.reject('Request is overridden');
}
this.pendingRequests.set(pendingRequest.id, pendingRequest);
logger.debug('%s Request is created %s, submit: %s, message: %o', this.localPeerId, pendingRequest, options.submit ? 'yes' : 'no', options.message);
try {
if (options.submit) {
// we need to make the request ongoing at the leader, so the follower original request will not timeout
// becasue of commiting the log entry
await this.submit(options.message);
}
else {
this.sendMessage(options.message, remotePeers);
}
const response = await pendingRequest.promise;
return response;
}
catch (error) {
this.purgeResponseForRequest(requestId);
throw error;
}
finally {
this.pendingRequests.delete(pendingRequest.id);
}
}
processResponse(message) {
if (!message.requestId || !message.sourceId) {
logger.warn('_processResponse(): Message does not have a requestId or sourceId. message: %o', message);
return;
}
const chunkedResponse = message.sequence !== undefined && message.lastMessage !== undefined;
const onlyOneChunkExists = message.sequence === 0 && message.lastMessage === true;
// console.warn("_responseReceived ", message);
if (chunkedResponse && !onlyOneChunkExists) {
const pendingResponseId = `${message.sourceId}#${message.requestId}`;
let pendingResponse = this.pendingResponses.get(pendingResponseId);
if (!pendingResponse) {
pendingResponse = new PendingResponse_1.PendingResponse({
requestId: message.requestId,
sourcePeerId: message.sourceId,
});
this.pendingResponses.set(pendingResponseId, pendingResponse);
}
pendingResponse.accept(message);
if (!pendingResponse.isReady) {
const pendingRequest = this.pendingRequests.get(message.requestId ?? 'notExists');
// let's postpone the timeout if we knoe responses are coming
if (pendingRequest) {
pendingRequest.postponeTimeout();
}
return;
}
if (!this.pendingResponses.delete(pendingResponseId)) {
logger.warn(`Unsuccessful deleting for pending response ${pendingResponseId}`);
}
const assembledResponse = pendingResponse.result;
if (!assembledResponse) {
logger.warn(`Undefined Assembled response, cannot make a response for request ${message.requestId}`, message);
return;
}
message = assembledResponse;
}
if (!message.requestId) {
logger.warn('response message does not have a requestId', message);
return;
}
const pendingRequest = this.pendingRequests.get(message.requestId);
if (!pendingRequest) {
logger.warn(`%s Cannot find pending request for requestId ${message.requestId}`, this.localPeerId, message);
return;
}
if (pendingRequest.completed) {
logger.warn('Response is received for an already completed request. pendingRequest: %o, response: %o', pendingRequest, message);
return;
}
logger.debug('%s Response is received %o', this.localPeerId, message);
pendingRequest.accept(message);
}
purgeResponseForRequest(requestId) {
const pendingResponseKeys = [];
for (const [key, pendingResponse] of this.pendingResponses) {
if (pendingResponse.requestId === requestId) {
pendingResponseKeys.push(key);
}
}
pendingResponseKeys.forEach((pendingResponseKey) => this.pendingResponses.delete(pendingResponseKey));
}
purgeResponseForRequests(requestIds) {
const requestIdSet = new Set(requestIds);
const pendingResponseKeys = [];
for (const [key, pendingResponse] of this.pendingResponses) {
if (requestIdSet.has(pendingResponse.requestId)) {
pendingResponseKeys.push(key);
}
}
pendingResponseKeys.forEach((pendingResponseKey) => this.pendingResponses.delete(pendingResponseKey));
}
}
exports.HamokGrid = HamokGrid;