Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use SessionStore? #108

Open
samvher opened this issue Feb 2, 2017 · 7 comments
Open

How to use SessionStore? #108

samvher opened this issue Feb 2, 2017 · 7 comments

Comments

@samvher
Copy link

samvher commented Feb 2, 2017

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
Copy link
Owner

agrafix commented Feb 3, 2017

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.

  • ss_runTx: how to lift your custom monad tx into IO. For the STM store, this is atomically
  • ss_loadSession: load a session given an id
  • ss_deleteSession: delete a session given an id
  • ss_storeSession: write a session
  • ss_toList: list all sessions
  • ss_filterSessions: keep only sessions that match the predicate
  • ss_mapSessions: apply function to all stored sessions

@samvher
Copy link
Author

samvher commented Feb 3, 2017

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
Copy link
Owner

agrafix commented Feb 3, 2017

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
Copy link
Author

samvher commented Feb 3, 2017

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
Copy link
Owner

agrafix commented Feb 3, 2017

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
Copy link

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
Copy link
Owner

agrafix commented May 9, 2018

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.

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

No branches or pull requests

3 participants