Skip to content

Commit f777e2c

Browse files
committed
perf(FOUR-30871): [50383] Slow response time in API PUT /api/1.0/tasks/{taskId}
Add raw repository queries and eager-loaded BPMN context to reduce I/O and N+1 queries during task completion. https://processmaker.atlassian.net/browse/FOUR-30871
1 parent 58a8d9b commit f777e2c

9 files changed

Lines changed: 888 additions & 12 deletions

File tree

‎ProcessMaker/Http/Controllers/Api/TaskController.php‎

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,21 @@
3434
use ProcessMaker\Models\UserResourceView;
3535
use ProcessMaker\Notifications\TaskReassignmentNotification;
3636
use ProcessMaker\Query\SyntaxError;
37+
use ProcessMaker\Repositories\ProcessExecutionRawRepository;
3738
use ProcessMaker\SanitizeHelper;
3839
use ProcessMaker\Traits\TaskControllerIndexMethods;
3940

4041
class TaskController extends Controller
4142
{
4243
use TaskControllerIndexMethods;
4344

45+
private ?ProcessExecutionRawRepository $processExecutionRaw = null;
46+
47+
private function processExecutionRaw(): ProcessExecutionRawRepository
48+
{
49+
return $this->processExecutionRaw ??= app(ProcessExecutionRawRepository::class);
50+
}
51+
4452
/**
4553
* A whitelist of attributes that should not be
4654
* sanitized by our SanitizeInput middleware.
@@ -354,28 +362,48 @@ public function show(ProcessRequestToken $task)
354362
*/
355363
public function update(Request $request, ProcessRequestToken $task)
356364
{
365+
if (!$task->relationLoaded('process')) {
366+
$task->setRelation('process', $this->processExecutionRaw()->getProcessForAuthorizeRaw($task->process_id));
367+
}
357368
$this->authorize('update', $task);
358369
if ($request->input('status') === 'COMPLETED') {
359370
if ($task->status === 'CLOSED') {
360371
return abort(422, __('Task already closed'));
361372
}
362373
// Skip ConvertEmptyStringsToNull and TrimStrings middlewares
363374
$data = json_optimize_decode($request->getContent(), true);
364-
$data = SanitizeHelper::sanitizeData($data['data'], null, $task->processRequest->do_not_sanitize ?? []);
375+
$instance = $this->processExecutionRaw()->getProcessRequestForCompleteRaw($task->process_request_id);
376+
$data = SanitizeHelper::sanitizeData($data['data'], null, $instance->do_not_sanitize ?? []);
377+
365378
//Call the manager to trigger the start event
366-
$process = $task->process;
367-
$instance = $task->processRequest;
368-
TaskDraft::moveDraftFiles($task);
379+
$process = $this->processExecutionRaw()->getProcessForCompleteRaw($task->process_id);
380+
$instance->setRelation('process', $process);
381+
if ($processVersion = $this->processExecutionRaw()->getProcessVersionForCompleteRaw($instance->process_version_id)) {
382+
$instance->setRelation('processVersion', $processVersion);
383+
}
384+
$task->setRelation('processRequest', $instance);
385+
$task->setRelation('process', $process);
386+
387+
if ($this->processExecutionRaw()->taskHasDraftRaw($task->id)) {
388+
TaskDraft::moveDraftFiles($task);
389+
}
390+
369391
WorkflowManager::completeTask($process, $instance, $task, $data);
370392

371-
return new Resource($task->refresh());
393+
$responseInstance = $this->processExecutionRaw()->getProcessRequestForResponseRaw($task->process_request_id);
394+
$responseInstance->setRelation('process', $process);
395+
$taskRefreshed = $this->processExecutionRaw()->refreshTaskRaw($task, $process, $responseInstance);
396+
397+
return new Resource($taskRefreshed);
372398
} elseif (!empty($request->input('user_id'))) {
399+
$process = $this->processExecutionRaw()->getProcessForReassignRaw($task->process_id);
400+
$task->setRelation('process', $process);
401+
373402
$userToAssign = $request->input('user_id');
374403
$comments = $request->input('comments');
375404
$task->reassign($userToAssign, $request->user(), $comments);
376405

377-
$taskRefreshed = $task->refresh();
378-
406+
$taskRefreshed = $this->processExecutionRaw()->refreshTaskRaw($task, $process, $task->processRequest);
379407
CaseUpdate::dispatchSync($task->processRequest, $taskRefreshed);
380408

381409
return new Resource($taskRefreshed);

‎ProcessMaker/Jobs/BpmnAction.php‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,8 @@ private function findInstanceWithRetry($instanceId, bool $lightweight = false)
338338
}
339339

340340
/**
341-
* Load only lock and revision metadata when validating the fast path.
341+
* Load ProcessRequest for BPMN actions. In lightweight mode, only lock and
342+
* revision metadata are loaded when validating the fast path.
342343
*/
343344
private function findInstance($instanceId, bool $lightweight): ProcessRequest
344345
{
@@ -349,6 +350,12 @@ private function findInstance($instanceId, bool $lightweight): ProcessRequest
349350
'process_collaboration_id',
350351
'execution_revision',
351352
]);
353+
} else {
354+
$query->with([
355+
'process',
356+
'processVersion',
357+
'collaboration',
358+
]);
352359
}
353360

354361
return $query->findOrFail($instanceId);

‎ProcessMaker/Models/Process.php‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use ProcessMaker\Nayra\Managers\WorkflowManagerDefault;
3333
use ProcessMaker\Nayra\Storage\BpmnDocument;
3434
use ProcessMaker\Package\WebEntry\Models\WebentryRoute;
35+
use ProcessMaker\Repositories\ProcessExecutionRawRepository;
3536
use ProcessMaker\Rules\BPMNValidation;
3637
use ProcessMaker\Traits\Exportable;
3738
use ProcessMaker\Traits\ExtendedPMQL;
@@ -668,6 +669,14 @@ public function getNextUser(ActivityInterface $activity, ProcessRequestToken $to
668669
return $this->checkAssignment($token->getInstance(), $activity, $assignmentType, $escalateToManager, $user ? User::where('id', $user)->first() : null, $token);
669670
}
670671

672+
/**
673+
* @deprecated Use ProcessExecutionRawRepository::getNextUserRaw()
674+
*/
675+
public function getNextUserRaw(ActivityInterface $activity, ProcessRequestToken $token)
676+
{
677+
return app(ProcessExecutionRawRepository::class)->getNextUserRaw($this, $activity, $token);
678+
}
679+
671680
/**
672681
* If user assignment is not valid reassign to Process Manager
673682
*
@@ -708,7 +717,7 @@ private function checkAssignment(ProcessRequest $request, ActivityInterface $act
708717
return $user;
709718
}
710719

711-
private function scalateToManagerIfEnabled($user, $activity, $token, $assignmentType)
720+
public function scalateToManagerIfEnabled($user, $activity, $token, $assignmentType)
712721
{
713722
if ($user) {
714723
$assignmentProcess = self::where('name', self::ASSIGNMENT_PROCESS)->first();

‎ProcessMaker/Models/ProcessRequest.php‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -850,8 +850,13 @@ public function updateCatchEvents()
850850
public function mergeLatestStoredData()
851851
{
852852
$store = $this->getDataStore();
853-
$latest = self::select('data')->find($this->getId());
854-
$this->data = $store->updateArray($latest->data);
853+
// Load only the data column (Eloquent cast applies) without hydrating the full row.
854+
$latest = static::query()->whereKey($this->getKey())->first(['data']);
855+
$latestData = $latest?->data ?? [];
856+
if (!is_array($latestData)) {
857+
$latestData = [];
858+
}
859+
$this->data = $store->updateArray($latestData);
855860

856861
return $this->data;
857862
}

0 commit comments

Comments
 (0)