Created
July 28, 2014 14:16
-
-
Save etcimon/324f4c79c49b10f67610 to your computer and use it in GitHub Desktop.
Redis Session legacy code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SessionStore sessionStore; | |
RedisClient redis; | |
TaskLocal!RedisDatabase conn; | |
TaskLocal!(RedisVar!ActiveSession) activeSession; | |
shared static this(){ | |
auto memorySessionStore = new MemorySessionStore; | |
sessionStore = memorySessionStore; | |
settings.sessionStore = memorySessionStore; | |
} | |
struct ActiveSession | |
{ | |
enum Prefix = "FF_sess_"; | |
string session_id; // primary key | |
RedisVar!Order cart; | |
RedisVar!User user; | |
SysTime created; | |
SysTime last_activity; | |
} | |
struct User | |
{ | |
enum Prefix = "FF_user_"; | |
string email; // primary key | |
string password; | |
long credits; | |
RedisVar!Order cart; | |
RedisVar!(Order)[] pending; | |
RedisVar!(Order)[] history; | |
RedisVar!Profile profile; | |
} | |
void init(HTTPServerRequest req, HTTPServerResponse res) | |
{ | |
string sessionId; | |
if (req.cookies.get("SID", null) !is null){ | |
sessionId = req.cookies["SID"]; | |
} | |
if (!sessionId){ | |
req.session = res.startSession(); | |
sessionId = req.session.id; | |
} | |
conn = redis.getDatabase(0); | |
activeSession.key = sessionId; | |
if (!activeSession.exists) | |
{ | |
ActiveSession tmp_activeSession; | |
tmp_activeSession.session_id = sessionId; | |
tmp_activeSession.created = Clock.currTime(); | |
tmp_activeSession.last_activity = Clock.currTime(); | |
activeSession.storage.save(tmp_activeSession); | |
} | |
else { | |
ActiveSession tmp_activeSession = activeSession.storage.edit(); | |
tmp_activeSession.last_activity = Clock.currTime(); | |
activeSession.storage.save(tmp_activeSession); | |
} | |
} | |
struct RedisVar(T) | |
{ | |
private { | |
static string Prefix = T.Prefix; | |
T cachedVal; | |
SysTime cachedTime; | |
} | |
string key; | |
this(string k) | |
{ | |
key = k; | |
} | |
@property string id() const | |
{ | |
return key; | |
} | |
@property void id(UUID uuid) | |
{ | |
key = uuid.toString(); | |
} | |
@property bool exists() const | |
{ | |
return key !is null && conn.exists(Prefix ~ key); | |
} | |
@property T value() | |
{ | |
if (cachedTime !is cachedTime.init && Clock.currTime() - cachedTime > 10.seconds) | |
cachedVal = T.init; | |
else if (cachedVal !is T.init) | |
return cachedVal; | |
import std.stdio; | |
string val = conn.get!string(Prefix ~ key); | |
if (val is null) | |
{ | |
return T.init; | |
} | |
cachedVal = deserializeJson!T(val); | |
return cachedVal; | |
} | |
T edit() { | |
return this.value; | |
} | |
void save(T tmp) { | |
this.value = tmp; | |
} | |
@property void value(T val) | |
{ | |
cachedVal = val; | |
string serialized = serializeToJson(val).toString(); | |
conn.set!string(Prefix ~ key, serialized); | |
} | |
void opAssign(T val) | |
{ | |
this.value = val; | |
} | |
static RedisVar!T fromJson(Json value) | |
{ | |
return RedisVar!T(value.key.get!string); | |
} | |
Json toJson() const | |
{ | |
auto ret = Json.emptyObject; | |
ret.key = key; | |
return ret; | |
} | |
} | |
unittest { | |
// This is how the active session is used to refer to underlying objects | |
RedisVar!Order redis_cart = activeSession.storage.value.cart; | |
// The edit() and save() methods make it more obvious what's going on | |
Order tmp_cart = redis_cart.edit(); | |
if (!tmp_cart) | |
{ | |
tmp_cart = new Order; | |
tmp_cart.user.key = activeSession.storage.value.user.key; | |
tmp_cart.guid = randomUUID(); | |
redis_cart.id = tmp_cart.guid; | |
// update references in Session and User objects | |
// The indirections are lazy database requests that allow graph-like data to persist | |
setCartUUID(tmp_cart.guid); | |
redis_cart.save(tmp_cart); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I should probably add some locking in edit() using a redis
conn.getSet(1) == 0
with a sleep-loop if it fails, multiple locks must be implemented using a multi-get-set to avoid dead locks. That would have to be embedded in the RedisClient using transactions