Skip to content

Commit

Permalink
[backend] handle locks in draft
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyCloarec committed Sep 30, 2024
1 parent 9b281cd commit f9989d3
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 8 deletions.
11 changes: 6 additions & 5 deletions opencti-platform/opencti-graphql/src/database/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ import {
import { buildEntityData, buildInnerRelation, buildRelationData } from './data-builder';
import { deleteAllObjectFiles, moveAllFilesFromEntityToAnother, uploadToStorage } from './file-storage-helper';
import { storeFileConverter } from './file-storage';
import { inDraftContext } from '../utils/draftContext';

// region global variables
const MAX_BATCH_SIZE = 300;
Expand Down Expand Up @@ -1439,7 +1440,7 @@ export const mergeEntities = async (context, user, targetEntityId, sourceEntityI
let lock;
try {
// Lock the participants that will be merged
lock = await lockResource(participantIds);
lock = await lockResource(participantIds, { draftId: inDraftContext(context, user) });
// Entities must be fully loaded with admin user to resolve/move all dependencies
const initialInstance = await storeLoadByIdWithRefs(context, user, targetEntityId);
const target = { ...initialInstance };
Expand Down Expand Up @@ -1950,7 +1951,7 @@ export const updateAttributeMetaResolved = async (context, user, initial, inputs
const participantIds = R.uniq(locksIds.filter((e) => !locks.includes(e)));
try {
// Try to get the lock in redis
lock = await lockResource(participantIds);
lock = await lockResource(participantIds, { draftId: inDraftContext(context, user) });
// region handle attributes
// Only for StixCyberObservable
const lookingEntities = [];
Expand Down Expand Up @@ -2766,7 +2767,7 @@ export const createRelationRaw = async (context, user, rawInput, opts = {}) => {
const participantIds = inputIds.filter((e) => !locks.includes(e));
try {
// Try to get the lock in redis
lock = await lockResource(participantIds);
lock = await lockResource(participantIds, { draftId: inDraftContext(context, user) });
// region check existing relationship
const existingRelationships = await getExistingRelations(context, user, resolvedInput, opts);
let existingRelationship = null;
Expand Down Expand Up @@ -2941,7 +2942,7 @@ const createEntityRaw = async (context, user, rawInput, type, opts = {}) => {
let lock;
try {
// Try to get the lock in redis
lock = await lockResource(participantIds);
lock = await lockResource(participantIds, { draftId: inDraftContext(context, user) });
// Generate the internal id if needed
const standardId = resolvedInput.standard_id || generateStandardId(type, resolvedInput);
// Check if the entity exists, must be done with SYSTEM USER to really find it.
Expand Down Expand Up @@ -3157,7 +3158,7 @@ export const internalDeleteElementById = async (context, user, id, opts = {}) =>
const participantIds = [element.internal_id];
try {
// Try to get the lock in redis
lock = await lockResource(participantIds);
lock = await lockResource(participantIds, { draftId: inDraftContext(context, user) });
if (isStixRefRelationship(element.entity_type)) {
const referencesPromises = opts.references ? internalFindByIds(context, user, opts.references, { type: ENTITY_TYPE_EXTERNAL_REFERENCE }) : Promise.resolve([]);
const references = await Promise.all(referencesPromises);
Expand Down
7 changes: 4 additions & 3 deletions opencti-platform/opencti-graphql/src/database/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,8 @@ export const redisFetchLatestDeletions = async () => {
await getClientLock().zremrangebyscore('platform-deletions', '-inf', time - (5 * 1000));
return getClientLock().zrange('platform-deletions', 0, -1);
};
interface LockOptions { automaticExtension?: boolean, retryCount?: number }
const defaultLockOpts: LockOptions = { automaticExtension: true, retryCount: conf.get('app:concurrency:retry_count') };
interface LockOptions { automaticExtension?: boolean, retryCount?: number, draftId?: string }
const defaultLockOpts: LockOptions = { automaticExtension: true, retryCount: conf.get('app:concurrency:retry_count'), draftId: '' };
const getStackTrace = () => {
const obj: any = {};
Error.captureStackTrace(obj, getStackTrace);
Expand All @@ -366,7 +366,8 @@ export const lockResource = async (resources: Array<string>, opts: LockOptions =
let timeout: NodeJS.Timeout | undefined;
let extension: undefined | Promise<void>;
const initialCallStack = getStackTrace();
const locks = R.uniq(resources).map((id) => `{locks}:${id}`);
const draftId = opts.draftId ? opts.draftId : '';
const locks = R.uniq(resources).map((id) => `{locks}:${id}${draftId}`);
const automaticExtensionThreshold = conf.get('app:concurrency:extension_threshold');
const retryDelay = conf.get('app:concurrency:retry_delay');
const retryJitter = conf.get('app:concurrency:retry_jitter');
Expand Down

0 comments on commit f9989d3

Please sign in to comment.