fix(selfop): allow explicit retry of failed continuous inventory roots
This commit is contained in:
+19
-7
@@ -224,18 +224,23 @@ public class SelfopTaskService {
|
||||
lockBusinessAccount(id);
|
||||
var commandQuery=new QueryWrapper<SelfopSyncTask>().eq("source_account",preview.getSourceAccount()).eq("request_key","retry:"+requestKey);
|
||||
var prior=tasks.selectOne(commandQuery);
|
||||
if(prior!=null){require(prior.getRequestHash().equals(hash(List.of(id,requestKey,reason))),"SELFOP_IDEMPOTENCY_CONFLICT");businessGuard(preview);return preview;}
|
||||
if(prior!=null){require(prior.getRequestHash().equals(hash(List.of(id,requestKey,reason))),"SELFOP_IDEMPOTENCY_CONFLICT");businessGuard(preview,false);return preview;}
|
||||
// Match executor lock ordering: account/warehouse scope first, task second.
|
||||
String activeOwner;LocalDateTime activeUntil;
|
||||
if("ACCOUNT_DISCOVERY".equals(preview.getTaskRole())){var scope=locks.lockDiscovery(preview.getDiscoveryScopeId());require(scope!=null,"SELFOP_SCOPE_MISSING");activeOwner=scope.getLeaseOwner();activeUntil=scope.getLeaseUntilUtc();}
|
||||
else{var scope=locks.lockScope(preview.getScopeId());require(scope!=null,"SELFOP_SCOPE_MISSING");activeOwner=scope.getLeaseOwner();activeUntil=scope.getLeaseUntilUtc();}
|
||||
prior=tasks.selectOne(new QueryWrapper<SelfopSyncTask>().eq("source_account",preview.getSourceAccount()).eq("request_key","retry:"+requestKey).last("FOR UPDATE"));
|
||||
if(prior!=null){require(prior.getRequestHash().equals(hash(List.of(id,requestKey,reason))),"SELFOP_IDEMPOTENCY_CONFLICT");return tasks.selectById(id);}
|
||||
businessGuard(preview);
|
||||
businessGuard(preview,false,true);
|
||||
require(SelfopLeaseService.available(activeOwner,activeUntil,utcNow()),"SELFOP_RETRY_LEASE_BUSY");
|
||||
var original=locks.lockTask(id);require(original!=null,"SELFOP_TASK_MISSING");
|
||||
requireBusiness(original);requireExecution(config,original.getExecutionPurpose());
|
||||
require(!"SUCCEEDED".equals(original.getStatus()),"SELFOP_TASK_ALREADY_SUCCEEDED");
|
||||
// The audit creation checks the parent again. Persist the authorized resume in this same
|
||||
// transaction before creating it; any audit failure rolls the resume back as well.
|
||||
original.setStatus("READY");original.setNextRetryAtUtc(utcNow());
|
||||
if(!continuousBusinessRoot(original))original.setRequestedBy(operator);
|
||||
original.setUpdatedAt(utcNow());tasks.updateById(original);
|
||||
// Resume original logical task, retaining apply barrier and stage set. Record the manual command separately.
|
||||
var audit=new SelfopSyncTask();audit.setSourceAccount(original.getSourceAccount());audit.setTaskRole(original.getTaskRole());audit.setScopeId(original.getScopeId());
|
||||
audit.setExecutionPurpose(original.getExecutionPurpose());
|
||||
@@ -243,9 +248,6 @@ public class SelfopTaskService {
|
||||
audit.setRequestKey("retry:"+requestKey);audit.setRequestHash(hash(List.of(id,requestKey,reason)));audit.setTaskType("MANUAL_RETRY");audit.setScopeHash(original.getScopeHash());
|
||||
audit.setQueryScopeJson(json(Map.of("resumesTaskNo",original.getTaskNo(),"reason",reason)));audit.setStatus("SUCCEEDED");audit.setApplyState("NOT_APPLICABLE");audit.setRequestedBy(operator);
|
||||
create(audit);
|
||||
original.setStatus("READY");original.setNextRetryAtUtc(utcNow());
|
||||
if(!continuousBusinessRoot(original))original.setRequestedBy(operator);
|
||||
original.setUpdatedAt(utcNow());tasks.updateById(original);
|
||||
return original;
|
||||
}
|
||||
public List<SelfopSyncTask> due(int limit){return continuousBusiness(config)?locks.dueContinuous(utcNow(),limit,config.getSourceAccount()):locks.due(utcNow(),limit,config.getCollectionMode());}
|
||||
@@ -301,7 +303,8 @@ public class SelfopTaskService {
|
||||
var task=tasks.selectById(id);businessGuard(task);
|
||||
}
|
||||
private void businessGuard(SelfopSyncTask task){businessGuard(task,true);}
|
||||
private void businessGuard(SelfopSyncTask task,boolean terminate){
|
||||
private void businessGuard(SelfopSyncTask task,boolean terminate){businessGuard(task,terminate,false);}
|
||||
private void businessGuard(SelfopSyncTask task,boolean terminate,boolean inventoryRetry){
|
||||
if(!testBusiness(task))return;
|
||||
require(controlledBusinessTask(task,tasks::selectById,continuousBusiness(config)),"SELFOP_TEST_MANUAL_TASK_REQUIRED");
|
||||
require(task.getBusinessRootTaskId()!=null,"BUSINESS_SESSION_ROOT_REQUIRED");
|
||||
@@ -318,7 +321,7 @@ public class SelfopTaskService {
|
||||
var budget=read(root.getBusinessBudgetJson(),SelfopBusinessBudget.class);budget.validate();
|
||||
require(!budget.continuous||(continuousBusiness(config)&&continuousBusinessRoot(root)),"SELFOP_CONTINUOUS_COLLECTION_DISABLED");
|
||||
String reason=budget.stopReason;
|
||||
if(reason==null&&"DEAD".equals(root.getStatus()))reason="BUSINESS_SESSION_STOPPED";
|
||||
if(reason==null&&"DEAD".equals(root.getStatus())&&!(inventoryRetry&&failedInventoryScanRoot(root)))reason="BUSINESS_SESSION_STOPPED";
|
||||
if(reason==null)return;
|
||||
if(!terminate)throw new BusinessSessionStoppedException(reason);
|
||||
var now=utcNow();budget.stopReason=reason;
|
||||
@@ -331,6 +334,15 @@ public class SelfopTaskService {
|
||||
.set("status","DEAD").set("next_retry_at_utc",null).set("updated_at",now));
|
||||
throw new BusinessSessionStoppedException(reason);
|
||||
}
|
||||
private boolean failedInventoryScanRoot(SelfopSyncTask root){
|
||||
if(!continuousBusiness(config)||!continuousBusinessRoot(root)||!"WAREHOUSE_SYNC".equals(root.getTaskRole()))return false;
|
||||
var scope=scopes.selectById(root.getScopeId());
|
||||
if(scope==null||!"INVENTORY".equals(scope.getDataFamily()))return false;
|
||||
var latest=runs.selectOne(new QueryWrapper<SelfopSyncRun>().eq("task_id",root.getId())
|
||||
.orderByDesc("attempt_no","id").last("LIMIT 1"));
|
||||
return latest!=null&&"FAILED".equals(latest.getStatus())
|
||||
&&Set.of("SELFOP_STOCK_DUPLICATE","SELFOP_STOCK_COUNT_MISMATCH","SELFOP_PAGE_TOTAL_CHANGED").contains(latest.getErrorCode());
|
||||
}
|
||||
private static void checkTaskLease(SelfopSyncTask task,SelfopLeaseService.Lease lease){
|
||||
require(task!=null&&(lease.account()?"ACCOUNT_DISCOVERY".equals(task.getTaskRole())&&Objects.equals(task.getDiscoveryScopeId(),lease.scopeId())
|
||||
:"WAREHOUSE_SYNC".equals(task.getTaskRole())&&Objects.equals(task.getScopeId(),lease.scopeId())),"SELFOP_TASK_LEASE_SCOPE_MISMATCH");
|
||||
|
||||
Reference in New Issue
Block a user