@medusajs/index
Version:
Medusa Index module
238 lines • 13.5 kB
JavaScript
"use strict";
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _DataSynchronizer_instances, _DataSynchronizer_container, _DataSynchronizer_isReady, _DataSynchronizer_schemaObjectRepresentation, _DataSynchronizer_storageProvider, _DataSynchronizer_orchestrator, _DataSynchronizer_query_get, _DataSynchronizer_locking_get, _DataSynchronizer_indexMetadataService_get, _DataSynchronizer_indexSyncService_get, _DataSynchronizer_indexRelationService_get, _DataSynchronizer_logger_get, _DataSynchronizer_isReadyOrThrow, _DataSynchronizer_updatedStatus, _DataSynchronizer_taskRunner;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataSynchronizer = void 0;
const utils_1 = require("@medusajs/framework/utils");
const _utils_1 = require("../utils");
const promises_1 = require("timers/promises");
class DataSynchronizer {
constructor(container) {
_DataSynchronizer_instances.add(this);
_DataSynchronizer_container.set(this, void 0);
_DataSynchronizer_isReady.set(this, false);
_DataSynchronizer_schemaObjectRepresentation.set(this, void 0);
_DataSynchronizer_storageProvider.set(this, void 0);
_DataSynchronizer_orchestrator.set(this, void 0);
__classPrivateFieldSet(this, _DataSynchronizer_container, container, "f");
}
onApplicationStart({ schemaObjectRepresentation, storageProvider, }) {
__classPrivateFieldSet(this, _DataSynchronizer_storageProvider, storageProvider, "f");
__classPrivateFieldSet(this, _DataSynchronizer_schemaObjectRepresentation, schemaObjectRepresentation, "f");
__classPrivateFieldSet(this, _DataSynchronizer_isReady, true, "f");
}
async syncEntities(entities, lockDuration = 60 // 1 minute
) {
__classPrivateFieldGet(this, _DataSynchronizer_instances, "m", _DataSynchronizer_isReadyOrThrow).call(this);
const entitiesToSync = entities.map((entity) => entity.entity);
__classPrivateFieldSet(this, _DataSynchronizer_orchestrator, new _utils_1.Orchestrator(__classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_locking_get), entitiesToSync, {
lockDuration,
logger: __classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_logger_get),
}), "f");
await __classPrivateFieldGet(this, _DataSynchronizer_orchestrator, "f").process(__classPrivateFieldGet(this, _DataSynchronizer_instances, "m", _DataSynchronizer_taskRunner).bind(this));
}
async removeEntities(entities, staleOnly = false) {
__classPrivateFieldGet(this, _DataSynchronizer_instances, "m", _DataSynchronizer_isReadyOrThrow).call(this);
const staleCondition = staleOnly ? "staled_at IS NOT NULL" : "";
for (const entity of entities) {
await __classPrivateFieldGet(this, _DataSynchronizer_container, "f").manager.execute(`WITH deleted_data AS (
DELETE FROM "index_data"
WHERE "name" = ? ${staleCondition ? `AND ${staleCondition}` : ""}
RETURNING id
)
DELETE FROM "index_relation"
WHERE ("parent_name" = ? AND "parent_id" IN (SELECT id FROM deleted_data))
OR ("child_name" = ? AND "child_id" IN (SELECT id FROM deleted_data))`, [entity, entity, entity]);
}
}
async syncEntity({ entityName, pagination = {}, ack, }) {
__classPrivateFieldGet(this, _DataSynchronizer_instances, "m", _DataSynchronizer_isReadyOrThrow).call(this);
const schemaEntityObjectRepresentation = __classPrivateFieldGet(this, _DataSynchronizer_schemaObjectRepresentation, "f")[entityName];
const { alias, moduleConfig } = schemaEntityObjectRepresentation;
const isLink = !!moduleConfig?.isLink;
if (!alias) {
__classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_logger_get).info(`[Index engine] Cannot find Entity '${entityName}' alias. Skipping.`);
const acknoledgement = {
lastCursor: pagination.cursor ?? null,
done: true,
};
await ack(acknoledgement);
return acknoledgement;
}
const entityPrimaryKey = "id";
const moduleHasId = !!moduleConfig?.primaryKeys?.includes("id");
if (!moduleHasId) {
const acknoledgement = {
lastCursor: pagination.cursor ?? null,
err: new Error(`Entity ${entityName} does not have a property 'id'. The 'id' must be provided and must be orderable (e.g ulid)`),
};
await ack(acknoledgement);
return acknoledgement;
}
let processed = 0;
let currentCursor = pagination.cursor;
const batchSize = Math.min(pagination.batchSize ?? 100, 100);
const limit = pagination.limit ?? Infinity;
let error = null;
while (processed < limit) {
const filters = {};
if (currentCursor) {
filters[entityPrimaryKey] = { $gt: currentCursor };
}
if (pagination.updated_at) {
filters["updated_at"] = { $gt: pagination.updated_at };
}
const queryResult = await __classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_query_get).graph({
entity: alias,
fields: [entityPrimaryKey],
filters,
pagination: {
order: {
[entityPrimaryKey]: "asc",
},
take: batchSize,
},
});
if (!queryResult?.data?.length) {
break;
}
const data = queryResult.data;
const envelop = {
data,
name: !isLink
? `*.${utils_1.CommonEvents.CREATED}`
: `*.${utils_1.CommonEvents.ATTACHED}`,
};
try {
await __classPrivateFieldGet(this, _DataSynchronizer_storageProvider, "f").consumeEvent(schemaEntityObjectRepresentation)(envelop);
currentCursor = data[data.length - 1][entityPrimaryKey];
processed += data.length;
await ack({ lastCursor: currentCursor });
}
catch (err) {
error = err;
break;
}
await (0, promises_1.setTimeout)(0, undefined, { ref: false });
}
let acknoledgement = {
lastCursor: currentCursor,
done: true,
};
if (error) {
acknoledgement = {
lastCursor: currentCursor,
err: error,
};
await ack(acknoledgement);
return acknoledgement;
}
await ack(acknoledgement);
return acknoledgement;
}
}
exports.DataSynchronizer = DataSynchronizer;
_DataSynchronizer_container = new WeakMap(), _DataSynchronizer_isReady = new WeakMap(), _DataSynchronizer_schemaObjectRepresentation = new WeakMap(), _DataSynchronizer_storageProvider = new WeakMap(), _DataSynchronizer_orchestrator = new WeakMap(), _DataSynchronizer_instances = new WeakSet(), _DataSynchronizer_query_get = function _DataSynchronizer_query_get() {
return __classPrivateFieldGet(this, _DataSynchronizer_container, "f")[utils_1.ContainerRegistrationKeys.QUERY];
}, _DataSynchronizer_locking_get = function _DataSynchronizer_locking_get() {
return __classPrivateFieldGet(this, _DataSynchronizer_container, "f")[utils_1.Modules.LOCKING];
}, _DataSynchronizer_indexMetadataService_get = function _DataSynchronizer_indexMetadataService_get() {
return __classPrivateFieldGet(this, _DataSynchronizer_container, "f").indexMetadataService;
}, _DataSynchronizer_indexSyncService_get = function _DataSynchronizer_indexSyncService_get() {
return __classPrivateFieldGet(this, _DataSynchronizer_container, "f").indexSyncService;
}, _DataSynchronizer_indexRelationService_get = function _DataSynchronizer_indexRelationService_get() {
return __classPrivateFieldGet(this, _DataSynchronizer_container, "f").indexRelationService;
}, _DataSynchronizer_logger_get = function _DataSynchronizer_logger_get() {
try {
return __classPrivateFieldGet(this, _DataSynchronizer_container, "f").logger;
}
catch (err) {
return console;
}
}, _DataSynchronizer_isReadyOrThrow = function _DataSynchronizer_isReadyOrThrow() {
if (!__classPrivateFieldGet(this, _DataSynchronizer_isReady, "f")) {
throw new Error("DataSynchronizer is not ready. Call onApplicationStart first.");
}
}, _DataSynchronizer_updatedStatus = async function _DataSynchronizer_updatedStatus(entity, status) {
await __classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_indexMetadataService_get).update({
data: {
status,
},
selector: {
entity,
},
});
}, _DataSynchronizer_taskRunner = async function _DataSynchronizer_taskRunner(entity) {
__classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_logger_get).info(`[Index engine] syncing entity '${entity}'`);
const [[lastCursor]] = await (0, utils_1.promiseAll)([
__classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_indexSyncService_get).list({
entity,
}, {
select: ["last_key"],
}),
__classPrivateFieldGet(this, _DataSynchronizer_instances, "m", _DataSynchronizer_updatedStatus).call(this, entity, _utils_1.IndexMetadataStatus.PROCESSING),
__classPrivateFieldGet(this, _DataSynchronizer_container, "f").manager.execute(`UPDATE "index_data" SET "staled_at" = NOW() WHERE "name" = ?`, [entity]),
]);
let startTime = performance.now();
let chunkStartTime = startTime;
const finalAcknoledgement = await this.syncEntity({
entityName: entity,
pagination: {
cursor: lastCursor?.last_key,
},
ack: async (ack) => {
const endTime = performance.now();
const chunkElapsedTime = (endTime - chunkStartTime).toFixed(2);
if (ack.lastCursor) {
__classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_logger_get).debug(`[Index engine] syncing entity '${entity}' updating last cursor to ${ack.lastCursor} (+${chunkElapsedTime}ms)`);
await __classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_indexSyncService_get).update({
data: {
last_key: ack.lastCursor,
},
selector: {
entity,
},
});
if (!ack.done && !ack.err) {
await __classPrivateFieldGet(this, _DataSynchronizer_orchestrator, "f").renewLock(entity);
}
}
if (ack.err) {
__classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_logger_get).error(`[Index engine] syncing entity '${entity}' failed with error (+${chunkElapsedTime}ms):\n${ack.err.message}`);
}
if (ack.done) {
const elapsedTime = (endTime - startTime).toFixed(2);
__classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_logger_get).info(`[Index engine] syncing entity '${entity}' done (+${elapsedTime}ms)`);
}
chunkStartTime = performance.now();
},
});
if (finalAcknoledgement.done) {
await (0, utils_1.promiseAll)([
__classPrivateFieldGet(this, _DataSynchronizer_instances, "m", _DataSynchronizer_updatedStatus).call(this, entity, _utils_1.IndexMetadataStatus.DONE),
__classPrivateFieldGet(this, _DataSynchronizer_instances, "a", _DataSynchronizer_indexSyncService_get).update({
data: {
last_key: finalAcknoledgement.lastCursor,
},
selector: {
entity,
},
}),
this.removeEntities([entity], true),
]);
}
if (finalAcknoledgement.err) {
await __classPrivateFieldGet(this, _DataSynchronizer_instances, "m", _DataSynchronizer_updatedStatus).call(this, entity, _utils_1.IndexMetadataStatus.ERROR);
}
};
//# sourceMappingURL=data-synchronizer.js.map