Автоинкрементные ключи и хранение сессий в БД
Вчера разбирался с интересным багом с Zend_Session_Storage_DbTable. Хотя в конце выяснилось, что он то тут и не причем 🙂
Итак, веб-приложение на базе Zend Framework хранит сессии в бд MySQL с помощью Zend_Session_SaveHandler_DbTable. Структура таблицы аналогичная мануалу.
1 2 3 4 5 6 7 |
CREATE TABLE IF NOT EXISTS `dao_session` ( `id` char(32) NOT NULL DEFAULT '', `modified` int(20) DEFAULT NULL, `lifetime` int(20) DEFAULT NULL, `data` varbinary(4096) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MEMORY DEFAULT CHARSET=utf8 |
Единственное движок хранения мы выбрали MEMORY, чтобы сессии работали побыстрее. Перед нашим программистом встала задача, связать сессию с покупательскими корзинами, которые хранятся в другой таблице. Для этого необходим внешний ключ. Использовать поле id нельзя, т.к. в системе сессии создаются для гостя, и при логине происходит перегенерация id сессии, чтобы обновился ключ кэша блоков, и зарегистрированному пользователю не показывался закэшированный для него же логин-блок с пустыми полями логина пароля.
Было два варианта решения проблемы.
1. При перегенерации сессии, перезаписывать поле с id сессии в таблице с корзинами.
2. Добавить автоинкрементный ключ в таблицу session сделать к ней модель Doctrine, связать две таблицы внешними ключами и ничего не обновлять, т.к. связь будет идти через другое поле (автоинкрементный ключ). Вроде бы идея хорошая, но есть подводные камни.
После создания сессии, в контексте текущего HTTP-запроса нельзя получить автоинкрементный ключ, т.к. в момент выполнения скрипта, и вплоть до его завершения данные о сессии в БД не записываются. Запись данных происходит после завершения скрипта, с помощью вызова функции session_write_close(), которая завершает сессию и записывает данные. Иными словами нельзя записать данные сессии в хранилище (файл, БД) без (до) её закрытия!
Вот цитата из мануала:
void session_write_close ( void )End the current session and store session data.
Session data is usually stored after your script terminated without the need to call session_write_close(), but as session data is locked to prevent concurrent writes only one script may operate on a session at any time. When using framesets together with sessions you will experience the frames loading one by one due to this locking. You can reduce the time needed to load all the frames by ending the session as soon as all changes to session variables are done.
Естественно в следующий запрос данные будут, а вот в текущем нет. Так что будьте осторожнее, и не наступайте на эти грабли!
Полезно!) Спасибо за статьи по зенд)