Ticket #807 (enhancement)
Opened 4 months ago
Last modified 1 month ago
FileSession split by directory (PATCH INCLUDED)
Status: new
| Reported by: | theatrus@gmail.com | Assigned to: | no_mind |
|---|---|---|---|
| Priority: | normal | Milestone: | 3.2 |
| Component: | sessions | Keywords: | |
| Cc: |
Busy CherryPy sites running with FileSession? can generate tens of thousands of user sessions in the FileSession? store. FileSession? stores all of these in a single directory, which is not optimal for some file systems.
This patch addresses this problem by splitting the sessions in up to 256 dynamically created directories based on their first byte of ID (aa, 23, etc).
This patch is against 3.0.3
---
cherrypy/lib/sessions.py | 55 +++++++++++++++++++++++++++++++---------------
1 files changed, 37 insertions(+), 18 deletions(-)
diff --git a/cherrypy/lib/sessions.py b/cherrypy/lib/sessions.py
index 4e1676e..15aea8e 100644
--- a/cherrypy/lib/sessions.py
+++ b/cherrypy/lib/sessions.py
@@ -259,8 +259,16 @@ class FileSession(Session):
% (len(lockfiles), plural,
os.path.abspath(self.storage_path)))
+ def _get_directory(self):
+ begin_id = self.id[0:2]
+ f = os.path.join(self.storage_path, begin_id)
+ if not os.path.normpath(f).startswith(self.storage_path):
+ raise cherrypy.HTTPError(400, "Invalid session id in cookie.")
+ return f
+
def _get_file_path(self):
- f = os.path.join(self.storage_path, self.SESSION_PREFIX + self.id)
+ fdir = self._get_directory()
+ f = os.path.join(fdir, self.SESSION_PREFIX + self.id)
if not os.path.normpath(f).startswith(self.storage_path):
raise cherrypy.HTTPError(400, "Invalid session id in cookie.")
return f
@@ -277,7 +285,16 @@ class FileSession(Session):
except (IOError, EOFError):
return None
+ def _mkdir(self):
+ d = self._get_directory()
+
+ try:
+ os.mkdir(d) # Create a nested directory for this entry
+ except (OSError):
+ pass
+
def _save(self, expiration_time):
+
f = open(self._get_file_path(), "wb")
try:
pickle.dump((self._data, expiration_time), f)
@@ -292,6 +309,7 @@ class FileSession(Session):
def acquire_lock(self, path=None):
if path is None:
+ self._mkdir()
path = self._get_file_path()
path += self.LOCK_SUFFIX
while True:
@@ -314,23 +332,24 @@ class FileSession(Session):
"""Clean up expired sessions."""
now = datetime.datetime.now()
# Iterate over all session files in self.storage_path
- for fname in os.listdir(self.storage_path):
- if (fname.startswith(self.SESSION_PREFIX)
- and not fname.endswith(self.LOCK_SUFFIX)):
- # We have a session file: lock and load it and check
- # if it's expired. If it fails, nevermind.
- path = os.path.join(self.storage_path, fname)
- self.acquire_lock(path)
- try:
- contents = self._load(path)
- # _load returns None on IOError
- if contents is not None:
- data, expiration_time = contents
- if expiration_time < now:
- # Session expired: deleting it
- os.unlink(path)
- finally:
- self.release_lock(path)
+ for dname in os.listdir(self.storage_path): #iterate over directories
+ for fname in os.listdir(os.path.join(self.storage_path, self.dname)):
+ if (fname.startswith(self.SESSION_PREFIX)
+ and not fname.endswith(self.LOCK_SUFFIX)):
+ # We have a session file: lock and load it and check
+ # if it's expired. If it fails, nevermind.
+ path = os.path.join(self.storage_path, fname)
+ self.acquire_lock(path)
+ try:
+ contents = self._load(path)
+ # _load returns None on IOError
+ if contents is not None:
+ data, expiration_time = contents
+ if expiration_time < now:
+ # Session expired: deleting it
+ os.unlink(path)
+ finally:
+ self.release_lock(path)
class PostgresqlSession(Session):
Change History
05/25/08 17:25:37: Modified by fumanchu
- milestone changed from 3.0 to 3.2.
07/09/08 02:34:56: Modified by nick125
07/09/08 10:54:05: Modified by fumanchu
> Maybe this should be a separate Session handler altogether?
I tend to agree. Perhaps a subclass of FileHandler? if a good amount of code can be shared...


Maybe this should be a separate Session handler altogether? Providing I understand this patch correctly, I'm not sure that changing the FileSession? behavior like this would be a good idea. Maybe something such as "DirectorySession"?