Skip to content

Instantly share code, notes, and snippets.

@etcimon
Created July 28, 2014 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save etcimon/324f4c79c49b10f67610 to your computer and use it in GitHub Desktop.
Save etcimon/324f4c79c49b10f67610 to your computer and use it in GitHub Desktop.
Redis Session legacy code
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);
}
}
@etcimon
Copy link
Author

etcimon commented Jul 28, 2014

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment