db = $db ?: new Db(); } public function acquire(Draft $draft, int $timeout = 5, bool $preserve_existing_assignments = false): bool { return $this->acquireMany([$draft], $timeout, $preserve_existing_assignments); } /** * @param Draft[] $drafts */ public function acquireMany(array $drafts, int $timeout = 5, bool $preserve_existing_assignments = false): bool { foreach ($drafts as $draft) { if (!$draft instanceof Draft) continue; $draft->lockResourceAssignments(!$preserve_existing_assignments); } foreach ($this->lockKeys($drafts) as $key) { $locked = (int) $this->db->getVar( $this->db->prepare('SELECT GET_LOCK(%s, %d)', $key, $timeout) ); if ($locked !== 1) { $this->release(); return false; } $this->locks[] = $key; } return true; } public function release(): void { foreach (array_reverse($this->locks) as $key) { $this->db->getVar( $this->db->prepare('SELECT RELEASE_LOCK(%s)', $key) ); } $this->locks = []; } /** * @param Draft[] $drafts * @return string[] */ private function lockKeys(array $drafts): array { $keys = []; foreach ($drafts as $draft) { if (!$draft instanceof Draft) continue; foreach ($this->optionSessionKeys($draft) as $key) $keys[] = $key; foreach ($this->resourceKeys($draft) as $key) $keys[] = $key; } $keys = array_values(array_unique($keys)); sort($keys); return $keys; } /** * @return string[] */ private function optionSessionKeys(Draft $draft): array { $keys = []; $offering_id = $draft->getOffering()->id(); foreach ($draft->getOptions() as $option_id => $quantity) { if (absint($quantity) <= 0) continue; foreach ($draft->sessionsForOption(absint($option_id)) as $session) { $keys[] = $this->lockKey('option:' . $offering_id . ':' . absint($option_id) . ':' . $session); } } return $keys; } /** * @return string[] */ private function resourceKeys(Draft $draft): array { $keys = []; foreach ($draft->getResources() as $resource_id) { $resource_id = absint($resource_id); if ($resource_id <= 0) continue; $keys[] = $this->lockKey('resource:' . $resource_id); } return $keys; } private function lockKey(string $identity): string { return 'bu_' . substr(md5($this->db->wpdb()->prefix), 0, 8) . '_' . md5($identity); } }