CI=&get_instance();} public function enqueue($type,array$payload,$key,$queue='default',$priority=5,$max=3,$companyId=null){$row=$this->CI->db->get_where('app_jobs',array('idempotency_key'=>$key))->row();if($row)return(int)$row->id;$this->CI->db->insert('app_jobs',array('company_id'=>$companyId,'queue_name'=>$queue,'job_type'=>$type,'payload'=>json_encode($payload),'priority'=>$priority,'max_attempts'=>$max,'available_at'=>date('Y-m-d H:i:s'),'idempotency_key'=>$key,'created_at'=>date('Y-m-d H:i:s')));return(int)$this->CI->db->insert_id();} public function reserve($worker,$queue='default'){$db=$this->CI->db;$db->trans_begin();try{$job=$db->query("SELECT * FROM app_jobs WHERE queue_name=? AND status IN('queued','retry') AND available_at<=? ORDER BY priority ASC,id ASC LIMIT 1 FOR UPDATE",array($queue,date('Y-m-d H:i:s')))->row();if(!$job){$db->trans_commit();return null;}$db->where('id',$job->id)->where_in('status',array('queued','retry'))->update('app_jobs',array('status'=>'processing','attempts'=>$job->attempts+1,'locked_at'=>date('Y-m-d H:i:s'),'locked_by'=>$worker));if($db->affected_rows()!==1)throw new RuntimeException('Job telah diambil worker lain.');$db->trans_commit();$job->attempts++;return$job;}catch(Throwable$e){$db->trans_rollback();throw$e;}} public function recoverStale($seconds=900){$this->CI->db->where('status','processing')->where('locked_at <',date('Y-m-d H:i:s',time()-max(60,(int)$seconds)))->update('app_jobs',array('status'=>'retry','available_at'=>date('Y-m-d H:i:s'),'locked_at'=>null,'locked_by'=>null,'last_error'=>'Worker timeout; job dikembalikan ke antrean.'));return$this->CI->db->affected_rows();} public function complete($id){$this->CI->db->where('id',$id)->update('app_jobs',array('status'=>'completed','completed_at'=>date('Y-m-d H:i:s'),'locked_at'=>null,'locked_by'=>null));} public function fail($job,Throwable$error){$message=substr($error->getMessage(),0,4000);if((int)$job->attempts>=(int)$job->max_attempts){$this->CI->db->trans_start();$this->CI->db->insert('app_dead_letters',array('job_id'=>$job->id,'company_id'=>$job->company_id,'job_type'=>$job->job_type,'payload'=>$job->payload,'attempts'=>$job->attempts,'last_error'=>$message,'failed_at'=>date('Y-m-d H:i:s')));$this->CI->db->where('id',$job->id)->update('app_jobs',array('status'=>'dead','last_error'=>$message,'locked_at'=>null,'locked_by'=>null));$this->CI->db->trans_complete();return;}$delay=min(3600,pow(2,(int)$job->attempts)*30);$this->CI->db->where('id',$job->id)->update('app_jobs',array('status'=>'retry','last_error'=>$message,'available_at'=>date('Y-m-d H:i:s',time()+$delay),'locked_at'=>null,'locked_by'=>null));} }