function flt_flying_fleet_handler(&$config, $skip_fleet_update) { $flt_update_mode = 0; // 0 - old // 1 - new global $time_now; /* if(($time_now - $GLOBALS['config']->flt_lastUpdate <= 8 ) || $GLOBALS['skip_fleet_update']) { return; } $GLOBALS['config']->db_saveItem('flt_lastUpdate', $time_now); doquery('LOCK TABLE {{table}}aks WRITE, {{table}}rw WRITE, {{table}}errors WRITE, {{table}}messages WRITE, {{table}}fleets WRITE, {{table}}planets WRITE, {{table}}users WRITE, {{table}}logs WRITE, {{table}}iraks WRITE, {{table}}statpoints WRITE, {{table}}referrals WRITE, {{table}}counter WRITE'); */ if ($skip_fleet_update) { return; } switch ($flt_update_mode) { case 0: if ($time_now - $config->flt_lastUpdate <= 4) { return; } break; case 1: if ($config->flt_lastUpdate) { if ($time_now - $config->flt_lastUpdate <= 15) { return; } else { $GLOBALS['debug']->error('Flying fleet handler is on timeout', 'FFH Error', 504); } } break; } $config->db_saveItem('flt_lastUpdate', $time_now); doquery('START TRANSACTION;'); coe_o_missile_calculate(); $flt_user_cache = array(); $flt_fleet_cache = array(); $flt_event_cache = array(); $flt_planet_cache = array(); $_fleets = doquery("SELECT *, fleet_start_time AS fleet_time FROM `{{fleets}}` WHERE `fleet_start_time` <= '{$time_now}' FOR UPDATE;"); while ($fleet_row = mysql_fetch_assoc($_fleets)) { flt_cache_fleet($fleet_row, $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache, CACHE_ALL); } $_fleets = doquery("SELECT *, fleet_end_stay AS fleet_time FROM `{{fleets}}` WHERE `fleet_end_stay` <= '{$time_now}' AND fleet_end_stay > 0 FOR UPDATE;"); while ($fleet_row = mysql_fetch_assoc($_fleets)) { flt_cache_fleet($fleet_row, $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache, CACHE_ALL); } $_fleets = doquery("SELECT *, fleet_end_time AS fleet_time FROM `{{fleets}}` WHERE `fleet_end_time` <= '{$time_now}' FOR UPDATE;"); while ($fleet_row = mysql_fetch_assoc($_fleets)) { flt_cache_fleet($fleet_row, $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache, CACHE_ALL); } uasort($flt_event_cache, 'flt_flyingFleetsSort'); unset($_fleets); /* pdump(count($flt_user_cache), '$flt_user_row'); pdump(count($flt_planet_cache), '$flt_planet_row'); pdump(count($flt_fleet_cache), '$flt_fleet_cache'); pdump(count($flt_event_cache), '$flt_event_cache'); pdump($GLOBALS['time_now'], 'time_now'); die(); */ foreach ($flt_event_cache as $fleet_event_id => $fleet_event) { // pdump($fleet_event); // Checking data presence in cache flt_cache_fleet($fleet_event['fleet_id'], $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache, CACHE_ALL ^ CACHE_EVENT); //pdump($flt_fleet_cache[$fleet_event['fleet_id']]['fleet_mission'], 'fleet_mission'); //pdump($flt_fleet_cache[$fleet_event['fleet_id']]['fleet_start_time'], 'fleet_start_time'); //pdump($flt_fleet_cache[$fleet_event['fleet_id']]['fleet_end_time'], 'fleet_end_time'); //pdump($flt_fleet_cache[$fleet_event['fleet_id']]['fleet_mess'], 'fleet_mess'); $fleet_row = $flt_fleet_cache[$fleet_event['fleet_id']]; // There is no fleet for this event. It can be for fleets destroyed in battle, lost in expedition etc if (!$fleet_row) { continue; } $mission_data = array('fleet' => $flt_fleet_cache[$fleet_event['fleet_id']], 'src_user' => $flt_user_cache[$fleet_event['src_user_id']], 'src_planet' => $flt_planet_cache[$fleet_event['src_planet_hash']], 'dst_user' => $flt_user_cache[$fleet_event['dst_user_id']], 'dst_planet' => $flt_planet_cache[$fleet_event['dst_planet_hash']]); // Миссии должны возвращать измененные результаты, что бы второй раз не лезть в базу unset($mission_result); switch ($fleet_row['fleet_mission']) { // Для боевых атак нужно обновлять по САБу и по холду - таки надо возвращать данные из обработчика миссий! case MT_AKS: case MT_ATTACK: $attack_result = flt_mission_attack($mission_data); $mission_result = CACHE_COMBAT; break; case MT_DESTROY: $attack_result = flt_mission_destroy($mission_data); $mission_result = CACHE_COMBAT; break; case MT_COLONIZE: $mission_result = flt_mission_colonize($mission_data); break; case MT_EXPLORE: $mission_result = flt_mission_explore($mission_data); break; case MT_RELOCATE: $mission_result = flt_mission_relocate($mission_data); break; case MT_TRANSPORT: $mission_result = flt_mission_transport($mission_data); break; case MT_RECYCLE: $mission_result = flt_mission_recycle($mission_data); break; case MT_SPY: $mission_result = flt_mission_spy($mission_data); break; case MT_HOLD: $mission_result = flt_mission_hold($mission_data); break; case MT_MISSILE: // Missiles !! break; default: doquery("DELETE FROM `{{fleets}}` WHERE `fleet_id` = '{$fleet_row['fleet_id']}' LIMIT 1;"); break; } if ($attack_result) { // Case for passed attack $attack_result = $attack_result['rw'][0]; flt_unset_by_attack($attack_result['attackers'], $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache); flt_unset_by_attack($attack_result['defenders'], $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache); unset($attack_result); unset($flt_planet_cache[$fleet_event['dst_planet_hash']]); } else { // Unsetting data that we broken in mission handler if (($mission_result & CACHE_FLEET) == CACHE_FLEET) { unset($flt_fleet_cache[$fleet_event['fleet_id']]); } if (($mission_result & CACHE_USER_SRC) == CACHE_USER_SRC) { unset($flt_user_cache[$fleet_event['src_user_id']]); } if (($mission_result & CACHE_USER_DST) == CACHE_USER_DST) { unset($flt_user_cache[$fleet_event['dst_user_id']]); } if (($mission_result & CACHE_PLANET_SRC) == CACHE_PLANET_SRC) { unset($flt_planet_cache[$fleet_event['src_planet_hash']]); } if (($mission_result & CACHE_PLANET_DST) == CACHE_PLANET_DST) { unset($flt_planet_cache[$fleet_event['dst_planet_hash']]); } } } doquery('COMMIT;'); if ($flt_update_mode == 1) { $config->db_saveItem('flt_lastUpdate', 0); } }
function flt_flying_fleet_handler($skip_fleet_update = false) { /* [*] Нужно ли заворачивать ВСЕ в одну транзакцию? С одной стороны - да, что бы данные были гарантированно на момент снапшота С другой стороны - нет, потому что при большой активности это все будет блокировать слишком много рядов, да и таймаут будет большой для ожидания всего разлоченного Стоит завернуть каждую миссию отдельно? Это сильно увеличит количество запросов, зато так же сильно снизит количество блокировок. Resume: НЕТ! Надо оставить все в одной транзакции! Так можно будет поддерживать consistency кэша. Там буквально сантисекунды блокировки [*] Убрать кэшированние данных о пользователях и планета. Офигенно освободит память - проследить! НЕТ! Считать, скольким флотам нужна будет инфа и кэшировать только то, что используется больше раза! Заодно можно будет исключить перересчет очередей/ресурсов - сильно ускорит дело! Особенно будет актуально, когда все бонусы будут в одной таблице Ну и никто не заставляет как сейчас брать ВСЕ из таблицы - только по полям. Гемор, но не сильный - сделать запрос по группам sn_data И писать в БД только один раз результат [*] Нужно ли на этом этапе знать полную информацию о флотах? Заблокировать флоты можно и неполным запросом. Блокировка флотов - это не страшно. Ну, не пройдет одна-две отмены - так никто и не гарантировал реалтайма! С одной стороны - да, уменьшит количество запросов С другой стооны - расход памяти Все равно надо будет знать полную инфу о флоте в момент обработки [*] Сделать тестовую БД для расчетов [*] Но не раньше, чем переписать все миссии */ global $config, $debug; if ($config->game_disable != GAME_DISABLE_NONE || $skip_fleet_update) { return; } sn_db_transaction_start(); if ($config->db_loadItem('game_disable') != GAME_DISABLE_NONE || SN_TIME_NOW - strtotime($config->db_loadItem('fleet_update_last')) <= $config->fleet_update_interval) { sn_db_transaction_rollback(); return; } // Watchdog timer if ($config->db_loadItem('fleet_update_lock')) { if (SN_TIME_NOW - strtotime($config->fleet_update_lock) <= mt_rand(240, 300)) { sn_db_transaction_rollback(); return; } else { $debug->warning('Flying fleet handler was locked too long - watchdog unlocked', 'FFH Error', 504); } } $config->db_saveItem('fleet_update_lock', SN_TIME_SQL); $config->db_saveItem('fleet_update_last', SN_TIME_SQL); sn_db_transaction_commit(); //log_file('Начинаем обсчёт флотов'); //log_file('Обсчёт ракет'); sn_db_transaction_start(); coe_o_missile_calculate(); sn_db_transaction_commit(); $fleet_list = array(); $fleet_event_list = array(); $missions_used = array(); sn_db_transaction_start(); //log_file('Запрос на флоты'); $_fleets = doquery("SELECT * FROM `{{fleets}}` WHERE\n (`fleet_start_time` <= " . SN_TIME_NOW . " AND `fleet_mess` = 0)\n OR (`fleet_end_stay` <= " . SN_TIME_NOW . " AND fleet_end_stay > 0 AND `fleet_mess` = 0)\n OR (`fleet_end_time` <= " . SN_TIME_NOW . ")\n FOR UPDATE;"); //log_file('Выборка флотов'); while ($fleet_row = db_fetch($_fleets)) { set_time_limit(15); // Унифицировать код с темплейтным разбором эвентов на планете! $fleet_list[$fleet_row['fleet_id']] = $fleet_row; $missions_used[$fleet_row['fleet_mission']] = 1; if ($fleet_row['fleet_start_time'] <= SN_TIME_NOW && $fleet_row['fleet_mess'] == 0) { $fleet_event_list[] = array('fleet_row' => &$fleet_list[$fleet_row['fleet_id']], 'fleet_time' => $fleet_list[$fleet_row['fleet_id']]['fleet_start_time'], 'fleet_event' => EVENT_FLT_ARRIVE); } if ($fleet_row['fleet_end_stay'] > 0 && $fleet_row['fleet_end_stay'] <= SN_TIME_NOW && $fleet_row['fleet_mess'] == 0) { $fleet_event_list[] = array('fleet_row' => &$fleet_list[$fleet_row['fleet_id']], 'fleet_time' => $fleet_list[$fleet_row['fleet_id']]['fleet_end_stay'], 'fleet_event' => EVENT_FLT_ACOMPLISH); } if ($fleet_row['fleet_end_time'] <= SN_TIME_NOW) { $fleet_event_list[] = array('fleet_row' => &$fleet_list[$fleet_row['fleet_id']], 'fleet_time' => $fleet_list[$fleet_row['fleet_id']]['fleet_end_time'], 'fleet_event' => EVENT_FLT_RETURN); } } sn_db_transaction_commit(); //log_file('Сортировка и подгрузка модулей'); uasort($fleet_event_list, 'flt_flyingFleetsSort'); unset($_fleets); // TODO: Грузить только используемые модули из $missions_used $mission_files = array(MT_ATTACK => 'flt_mission_attack', MT_AKS => 'flt_mission_attack', MT_DESTROY => 'flt_mission_attack', MT_TRANSPORT => 'flt_mission_transport', MT_RELOCATE => 'flt_mission_relocate', MT_HOLD => 'flt_mission_hold', MT_SPY => 'flt_mission_spy', MT_COLONIZE => 'flt_mission_colonize', MT_RECYCLE => 'flt_mission_recycle', MT_EXPLORE => 'flt_mission_explore'); foreach ($missions_used as $mission_id => $cork) { require_once SN_ROOT_PHYSICAL . "includes/includes/{$mission_files[$mission_id]}" . DOT_PHP_EX; } //log_file('Обработка миссий'); $sn_groups_mission = sn_get_groups('missions'); foreach ($fleet_event_list as $fleet_event) { // TODO: Указатель тут потом сделать // TODO: СЕЙЧАС НАДО ПРОВЕРЯТЬ ПО БАЗЕ - А ЖИВОЙ ЛИ ФЛОТ?! $fleet_row = $fleet_event['fleet_row']; if (!$fleet_row) { // Fleet was destroyed in course of previous actions continue; } //log_file('Миссия'); // TODO Обернуть всё в транзакции. Начинать надо заранее, блокируя все таблицы внутренним локом SELECT 1 FROM {{users}} sn_db_transaction_start(); $config->db_saveItem('fleet_update_last', SN_TIME_SQL); $mission_data = $sn_groups_mission[$fleet_row['fleet_mission']]; // Формируем запрос, блокирующий сразу все нужные записи db_flying_fleet_lock($mission_data, $fleet_row); $fleet_row = doquery("SELECT * FROM {{fleets}} WHERE fleet_id = {$fleet_row['fleet_id']} FOR UPDATE", true); if (!$fleet_row || empty($fleet_row)) { // Fleet was destroyed in course of previous actions sn_db_transaction_commit(); continue; } if ($fleet_event['fleet_event'] == EVENT_FLT_RETURN) { // Fleet returns to planet RestoreFleetToPlanet($fleet_row, true, false, true); sn_db_transaction_commit(); continue; } if ($fleet_event['fleet_event'] == EVENT_FLT_ARRIVE && $fleet_row['fleet_mess'] != 0) { // При событии EVENT_FLT_ARRIVE флот всегда должен иметь fleet_mess == 0 // В противном случае это означает, что флот уже был обработан ранее - например, при САБе sn_db_transaction_commit(); continue; } // TODO: Здесь тоже указатели // TODO: Кэширование // TODO: Выбирать только нужные поля // шпионаж не дает нормальный ID fleet_end_planet_id 'dst_planet' $mission_data = array('fleet' => &$fleet_row, 'dst_user' => $mission_data['dst_user'] || $mission_data['dst_planet'] ? db_user_by_id($fleet_row['fleet_target_owner'], true) : null, 'dst_planet' => $mission_data['dst_planet'] ? db_planet_by_vector($fleet_row, 'fleet_end_', true, '`id`, `id_owner`, `name`') : null, 'src_user' => $mission_data['src_user'] || $mission_data['src_planet'] ? db_user_by_id($fleet_row['fleet_owner'], true) : null, 'src_planet' => $mission_data['src_planet'] ? db_planet_by_vector($fleet_row, 'fleet_start_', true, '`id`, `id_owner`, `name`') : null, 'fleet_event' => $fleet_event['fleet_event']); if ($mission_data['dst_planet']) { // $mission_data['dst_planet'] = sys_o_get_updated($mission_data['dst_user'], $mission_data['dst_planet']['id'], $fleet_row['fleet_start_time']); if ($mission_data['dst_planet']['id_owner']) { $mission_data['dst_planet'] = sys_o_get_updated($mission_data['dst_planet']['id_owner'], $mission_data['dst_planet']['id'], $fleet_row['fleet_start_time']); } $mission_data['dst_user'] = $mission_data['dst_user'] ? $mission_data['dst_planet']['user'] : null; $mission_data['dst_planet'] = $mission_data['dst_planet']['planet']; } switch ($fleet_row['fleet_mission']) { // Для боевых атак нужно обновлять по САБу и по холду - таки надо возвращать данные из обработчика миссий! case MT_AKS: case MT_ATTACK: case MT_DESTROY: $attack_result = flt_mission_attack($mission_data); $mission_result = CACHE_COMBAT; break; /* case MT_DESTROY: $attack_result = flt_mission_destroy($mission_data); $mission_result = CACHE_COMBAT; break; */ /* case MT_DESTROY: $attack_result = flt_mission_destroy($mission_data); $mission_result = CACHE_COMBAT; break; */ case MT_TRANSPORT: $mission_result = flt_mission_transport($mission_data); break; case MT_HOLD: $mission_result = flt_mission_hold($mission_data); break; case MT_RELOCATE: $mission_result = flt_mission_relocate($mission_data); break; case MT_EXPLORE: $mission_result = flt_mission_explore($mission_data); break; case MT_RECYCLE: $mission_result = flt_mission_recycle($mission_data); break; case MT_COLONIZE: $mission_result = flt_mission_colonize($mission_data); break; case MT_SPY: $mission_result = flt_mission_spy($mission_data); break; case MT_MISSILE: // Missiles !! break; // default: // doquery("DELETE FROM `{{fleets}}` WHERE `fleet_id` = '{$fleet_row['fleet_id']}' LIMIT 1;"); // break; } sn_db_transaction_commit(); } sn_db_transaction_start(); $config->db_saveItem('fleet_update_last', SN_TIME_SQL); $config->db_saveItem('fleet_update_lock', ''); sn_db_transaction_commit(); // log_file('Закончили обсчёт флотов'); }