agrafix / Spock

Another Haskell web framework for rapid development
https://www.spock.li
678 stars 56 forks source link

How to use SessionStore? #108

Open samvher opened 7 years ago

samvher commented 7 years ago

There seems to be little documentation other than the types :)

I want to make my sessions persistent, I was hoping I could just use readShowSessionPersist (for a development environment) but it seems that in the version I'm using (0.11.0.0) that has been removed. Do you have an example implementation?

agrafix commented 7 years ago

To implement a custom SessionStore, you'll have to "fill" this data-type:

data SessionStore sess tx
   = SessionStore
   { ss_runTx :: forall a. tx a -> IO a
   , ss_loadSession :: SessionId -> tx (Maybe sess)
   , ss_deleteSession :: SessionId -> tx ()
   , ss_storeSession :: sess -> tx ()
   , ss_toList :: tx [sess]
   , ss_filterSessions :: (sess -> Bool) -> tx ()
   , ss_mapSessions :: (sess -> tx sess) -> tx ()
   }

You can take a look here for the implementation of the default STM session store. Basically, a SessionStore sess tx is a store for sessions of type sess running in the monad tx.

samvher commented 7 years ago

Thanks, that helps. I was a bit overwhelmed because it looked a lot more complicated than just the pair of load/store functions in the older versions of Spock. Is there a specific reason the readShowSessionPersist is no longer available? Let's say, if I would implement it, would you want to add it to the code base?

Also, is there a way to use a SpockAction monad as tx? Because it seems a bit unpleasant to open a separate database connection (for example in the IO monad), when Spock already has a set-up for the connection in place. But at the same time I think the SpockAction monad depends on the SessionStore, right?

agrafix commented 7 years ago

I personally never used the readShowSessionPersist and wasn't aware that anyone used it. If you like to add it back it would make sense to have it sit on top of the stm version such that it only flushes the stm version's state to disk periodically and reads it once on launch. That way everything will stay performant. Alternatively one could also abstract over the read and show parts so that a user could theoretically also use safecopy for example.

Currently there is not since the WebState type of the SpockAction holds the session manager, but there's no reason not to pass the database down to the SessionStore. But here also keep in mind that hitting the database for every session/request will be expensive, so having a layer on top with stm or using an in-memory database like redis is attractive.

samvher commented 7 years ago

Ah ok. I just noticed that it existed, and I think it's an easy way to get persistent sessions in a dev environment (like now, I often restart my spock app, and it forgets that I'm logged in so I have to go through that process for testing, which takes time). Having to "derive (Read, Show)" on your session type is a much smaller up-front investment than implementing a whole session storage system. If the way you suggest could be both persistent and performant I think that could actually be a valuable addition (maybe then it would be realistic to avoid using the database for persistence altogether).

I'm not sure what you mean about abstracting over read and show though.

With passing the database down to the SessionStore, you mean generating it with a function of PoolOrConn a, passing it the same instance that's also used to configure SpockAction?

agrafix commented 7 years ago

By abstracting over read and show I meant that instead of using read and show in the code to parse/serialize to instead allow the user to provide a function parse :: BS.ByteString -> Either String a and serialize :: a-> BS.ByteString so that it's possible to use something other than read and show.

No. If you look at Web.Spock then all PoolOrConn a are converted into a connection pool. You can then pass this into createSessionManager for example.

elephanter commented 6 years ago

Hello, I tried to write custom SessionStore for Redis https://github.com/elephanter/spock-session-redis Is there better way than whole rewrite defaultSpockCfg method for Spock initialization with custom SessionStore? Also, Web.Spock.Config does not export errorHandler and errorTemplate methods, so I have to copy them. Maybe rename and export them as default methods?

agrafix commented 6 years ago

Oh, nice!

I think you can implement your custom defaultSpockCfg in terms of the existing defaultSpockCfg: You would call the existing one first and then simply swap out sc_store with your custom store.