Обработка сессий в ACMS
В новой версии движка (2.5.0) несколько изменился алгоритм "вычисления" сессии пользователя, надеюсь, что к лучшему. Напоминаю, что в движке предусмотрен свой собственный механизм поддержки сессий − более удобный, быстрый и надежный, нежели нативный механизм PHP.
Если на вашем сайте включена поддержка сессий (константа ACMSSessionEnabled), то при обработке каждого запроса нужно, первым делом, найти сессию данного посетителя. Причем подходящая сессия может отсутствовать, если посетитель только что зашел на сайт − в этом случае генерируется новая сессия. Но гораздо интереснее случай "подхвата" уже существующей сессии, т.е. поиск среди массива сессий, хранящихся в таблице sessions, той единственной и неповторимой, которая принадлежит этому посетителю.
Здесь надо учитывать несколько моментов:
Для облегчения поиска у каждой сессии есть свой уникальный идентификатор (он генерируется случайным образом при создании новой сессии), который также отсылается посетителю и сохраняется у него в куках. Но важно понимать, что этот ID, присланный посетителем, является лишь вспомогательной вещью. Доверять ему нельзя. Клиент может случайно или намеренно прислать неверный ID, у него могут быть отключены куки, ID может быть отрезан файрволлом и т. д.
Кроме ID, у каждой сессии также хранится IP адрес посетителя, и это уже критичный фактор. Ни в коем случае нельзя отдавать посетителю, "пришедшему" с одного адреса, сессию, в которой был сохранен другой адрес. Даже если он указал правильный ID сессии. В то же время возможно одновременное существование нескольких сессий с одним и тем же IP − например, если на сайте находится несколько посетителей от одного и того же провайдера с общим внешним адресом.
Сессия имеет привычку устаревать. Если посетитель не подает признаков жизни в течение 10 минут (константа ACMSSessionTimeout), его сессия автоматически удаляется. Отловить этот момент невозможно, поэтому, чтобы не проверять устаревшие сессии при каждом запросе, используется механизм сбора мусора. Примерно через каждые 100 запросов (константы ACMSSessionGCProbability, ACMSSessionGCDivisor) все устаревшие сессии удаляются из базы, а в промежутках между запусками сборщика мусора используются только те записи, которые "достаточно свежи" для работы. Для этого у каждой сессии также хранится дата-время последней активности посетителя.
Таким образом, когда мы пытаемся найти в базе подходящую сессию посетителя, нужно учитывать условия:
IP сессии должен обязательно совпадать с IP посетителя;
сессия обязательно должна быть достаточно "свежей";
желательно, чтобы ID сессии совпадал с тем ID, который прислал посетитель (если он вообще это сделал);
кроме того, если посетитель не прислал корректного ID, сессия не должна хранить никаких важных данных (не должна быть сессией авторизовавшегося участника, не должна содержать ни одного товара в корзине и тому подобное, в зависимости от конкретного сайта − управляется константами ACMSSessionCatchEnabled и ACMSSessionCatchRestrictions);
наконец, если таких сессий несколько, желательно брать самую свежую.
В предыдущей версии движок действовал следующим образом: выбирал из базы первую сессию, которая подходила под обязательные условия, затем проверял оставшиеся условия и либо использовал эту сессию, либо отбрасывал ее и создавал новую. К сожалению, этот метод оказался несовершенным. Некоторые боты, передавая неверный ID, вынуждали движок снова и снова отбрасывать найденную сессию и генерировать новую. Это приводило к большому количеству сессий, и люди жаловались на завышенное количество ежедневных "хостов" в статистике. Для версии 2.4.5 был выпущен патч, но он не решал проблемы полностью.
Теперь движок действует по другой схеме:
формирует список обязательных условий (IP + время);
выбирает из базы 10 (константа ACMSSessionCatchLimit) самых недавних сессий, подходящих под эти условия;
если среди этого набора есть сессия со значением ID, переданным посетителем, то используется эта сессия;
в противном случае используется самая свежая сессия, но которая не содержит никаких важных данных.
Как и раньше, если ни одной подходящей сессии так и не было найдено, генерируется новая сессия. Но судя по первым реальным тестам на работающих проектах (Симпсоны.Su, Инфосфера), новый механизм работает аккуратнее и экономнее прежнего, создавая новые сессии только в самом крайнем случае, когда становится точно известно, что на сайт зашел новый посетитель. Как следствие, подсчет количества хостов статистикой также ведется более точно.