@@ -1,12 +1,5 @@
1{-# LANGUAGE DeriveGeneric #-}
1{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE TypeFamilies #-}
7−{-# LANGUAGE TypeApplications #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
11module Foo.Data
4module Foo.Data
@@ -16,6 +9,7 @@ module Foo.Data
16 createApp,
9 createApp,
17 createConfig,
10 createConfig,
@@ -23,16 +17,12 @@ module Foo.Data
28 addConnection,
21 addConnection,
29 removeConnection,
22 removeConnection,
30 TokenizedResponse(..),
23 TokenizedResponse(..),
31 Update(..),
24 Update(..),
32− getToken,
25+ getToken
38import GHC.Conc
28import GHC.Conc
@@ -60,14 +50,6 @@ import qualified Data.CyclicBuffer as CB
60import Data.Set ((\\), union)
50import Data.Set ((\\), union)
61import Data.Monoid
51import Data.Monoid
62import Debug.Trace
52import Debug.Trace
64−import qualified Database.Beam.Postgres as P
65−import Database.Beam.Query
66−import Database.PostgreSQL.Simple (withTransaction)
67−import Database.Beam.Backend.SQL.BeamExtensions
68−import System.Random.Shuffle
69−import Network.SocketIO
70−import Network.EngineIO (SocketId)
72type App = ReaderT Config Handler
54type App = ReaderT Config Handler
@@ -75,35 +57,35 @@ class HasConfig c where
75 getCfg :: c -> Config
57 getCfg :: c -> Config
77allUsers :: App [User]
59allUsers :: App [User]
78−allUsers = asks dbConn >>= \conn -> liftIO . P.runBeamPostgres conn . runSelectReturningList . select . all_ . users $ socialDb
60+allUsers = ask >>= liftIO . fmap M.elems . readTVarIO . users
80post :: PostId -> App Post
62post :: PostId -> App Post
81post pId = do
63post pId = do
82− conn <- asks dbConn
64+ cfg <- ask
83− u <- liftIO . P.runBeamPostgres conn . runSelectReturningOne . flip lookup_ (PostId pId) . posts $ socialDb
65+ u <- liftIO . atomically . fmap (M.lookup pId) . readTVar . posts $ cfg
85 Just u -> return u
67 Just u -> return u
86 Nothing -> throwError err404
68 Nothing -> throwError err404
88user :: UserId -> App User
70user :: UserId -> App User
89user uId = do
71user uId = do
90− conn <- asks dbConn
72+ cfg <- ask
91− u <- liftIO . P.runBeamPostgres conn . runSelectReturningOne . flip lookup_ (UserId uId) . users $ socialDb
73+ u <- liftIO . atomically . fmap (M.lookup uId) . readTVar . users $ cfg
93 Just u -> return u
75 Just u -> return u
94 Nothing -> throwError err404
76 Nothing -> throwError err404
96−allPosts :: App [Post]
78+allPosts :: App (STM [Post])
97−allPosts = asks dbConn >>= \conn -> liftIO . P.runBeamPostgres conn . runSelectReturningList . select . all_ . posts $ socialDb
79+allPosts = asks $ fmap M.elems . readTVar . posts
99getToken :: App (STM Token)
81getToken :: App (STM Token)
100getToken = asks $ fmap CB.lastToken . readTVar . updates
82getToken = asks $ fmap CB.lastToken . readTVar . updates
102−createConfig :: P.Connection -> IO Config
84+createConfig :: IO Config
103−createConfig conn = do
85+createConfig = do
104 mng <- newManager tlsManagerSettings
86 mng <- newManager tlsManagerSettings
105− cfg <- atomically $ Config <$> return conn <*> return mng <*> newTVar M.empty <*> newTVar 0 <*> newTVar (CB.empty 20)
87+ cfg <- atomically $ Config <$> newTVar 0 <*> newTVar 0 <*> newTVar M.empty <*> newTVar M.empty <*> return mng <*> newTVar M.empty <*> newTVar 0 <*> newTVar (CB.empty 20)
106− forkIO . forever $ threadDelay 7000000 >> (runHandler . createApp cfg $ startAddingPosts 25 cfg >>= uncurry sendUpdate)
88+ forkIO . forever $ threadDelay 2000000 >> (runHandler . createApp cfg $ startAddingPosts 20 cfg >>= uncurry sendUpdate)
107 return cfg
89 return cfg
109type Token = Int
91type Token = Int
@@ -134,32 +116,18 @@ instance Semigroup Update where
134instance Monoid Update where
116instance Monoid Update where
135 mempty = Update S.empty S.empty
117 mempty = Update S.empty S.empty
137−addConnection :: HasConfig c => c -> Socket -> STM SocketId
119+addConnection :: HasConfig c => c -> Connection -> STM ConnectionId
138addConnection c conn = do
120addConnection c conn = do
139 let cfg = getCfg c
121 let cfg = getCfg c
140− let sId = socketId conn
141− -- lastID <- fmap (+1) . readTVar . lastConnection $ cfg
122+ lastID <- fmap (+1) . readTVar . lastConnection $ cfg
142− -- flip writeTVar lastID . lastConnection $ cfg
123+ flip writeTVar lastID . lastConnection $ cfg
143− modifyTVar (connections cfg) $ M.insert sId conn
124+ modifyTVar (connections cfg) $ M.insert lastID conn
144− return sId
125+ return lastID
146−removeConnection :: HasConfig c => c -> Socket -> STM ()
127+removeConnection :: HasConfig c => c -> ConnectionId -> STM ()
147−removeConnection c conn = do
128+removeConnection c cId = do
148 let cfg = getCfg c
129 let cfg = getCfg c
149− let sId = socketId conn
150− modifyTVar (connections cfg) $ M.delete sId
130+ modifyTVar (connections cfg) $ M.delete cId
153−-- subscribe :: MonadIO m => Config -> Socket -> m ()
154−-- subscribe cfg conn = do
155−-- cId <- liftSTM $ addConnection cfg conn
156−-- -- liftIO $ flip finally (atomically $ removeConnection cfg cId) $ forever $ do
157−-- -- _ <- receiveDataMessage conn
161−-- unsubscribe :: MonadIO m => Config -> Socket -> m ()
162−-- unsubscribe cfg conn = liftIO . atomically $ removeConnection cfg cId
@@ -171,9 +139,12 @@ type PostId = Int
171type ConnectionId = Int
139type ConnectionId = Int
173data Config = Config {
141data Config = Config {
174− dbConn :: P.Connection,
142+ lastUserID :: TVar UserId,
143+ lastPostID :: TVar PostId,
144+ users :: TVar (M.Map Int User),
145+ posts :: TVar (M.Map Int Post),
175 manager :: Manager,
146 manager :: Manager,
176− connections :: TVar (M.Map SocketId Socket),
147+ connections :: TVar (M.Map ConnectionId Connection),
177 lastConnection :: TVar ConnectionId,
148 lastConnection :: TVar ConnectionId,
178 updates :: TVar (CB.CyclicBuffer Update)
149 updates :: TVar (CB.CyclicBuffer Update)
@@ -181,149 +152,94 @@ data Config = Config {
181instance HasConfig Config where
152instance HasConfig Config where
182 getCfg = id
153 getCfg = id
155+-- startAddingUsers :: Config -> App ()
156+-- startAddingUsers cfg = do
157+-- a <- liftIO $ loremBacon (manager cfg) (Just AllMeat) (Just 2) (Just False)
158+-- let t = fromMaybe ["None", "None"] a
159+-- liftIO . atomically $ newUser cfg (t !! 0) (t !! 1)
184startAddingPosts :: Int -> Config -> App (Token, Update)
162startAddingPosts :: Int -> Config -> App (Token, Update)
185startAddingPosts lim cfg = do
163startAddingPosts lim cfg = do
186 cfg <- ask
164 cfg <- ask
187− let conn = dbConn cfg
188− -- let us = users socialDb
165+ let us = users cfg
189− -- let ps = posts socialDb
166+ let ps = posts cfg
191 a <- liftIO $ loremBacon (manager cfg) (Just AllMeat) (Just lim) (Just 0)
168 a <- liftIO $ loremBacon (manager cfg) (Just AllMeat) (Just lim) (Just 0)
192 g <- liftIO newStdGen
169 g <- liftIO newStdGen
193 let rns = randoms g
170 let rns = randoms g
195− let t = fromMaybe (repeat "None") a
199− (deletedIds, addedIds) <- join . liftIO . withTransaction conn . return $ do
172+ let t = fromMaybe (repeat "None") a
200− userIds <- liftIO . P.runBeamPostgres conn . runSelectReturningList . select $ do
173+ liftIO . atomically $ do
201− uc <- all_ . users $ socialDb
174+ uu <- readTVar us
202− pure (userId uc)
175+ let uSize = M.size uu
203−
176+ if uSize >= 1 then do
204− if length userIds <= 0 then return ([],[])
177+ pp <- readTVar ps
205− else do
178+ let toDeleteIndex = head rns `mod` M.size pp
206− postIds <- liftIO . P.runBeamPostgres conn . runSelectReturningList . select $ do
179+ let deletedKey = fst $ M.elemAt toDeleteIndex pp
207− uc <- all_ . posts $ socialDb
180+ dd <- if M.size pp > 0 then writeTVar ps (M.deleteAt toDeleteIndex pp) >> return [deletedKey]
208− pure (postId uc)
181+ else return []
211− let postCount = length postIds
212− let toDeleteIndecies = if postCount == 0 then [] else take deleteCount $ shuffle' postIds postCount g
214− let toAdd = max 0 $ lim - length postIds + length toDeleteIndecies
182+ let toAdd = max 0 $ lim - (M.size pp - 1)
215−
183+ add <- fmap (map postId) $ forM (zip rns $ take toAdd t) $ \(rnd, t) -> newPost cfg (randomFrom rnd uu) t
216− -- Just deletedPost <- liftIO . P.runBeamPostgres conn . runSelectReturningOne . select . limit_ 1 . offset_ (fromIntegral $ toDeleteIndex - 1) . all_ . posts $ socialDb
217− -- let deletedId = postId deletedPost
219− unless (length toDeleteIndecies <= 0) $
220− liftIO . P.runBeamPostgres conn . runDelete $
221− delete (posts socialDb) ((`in_` map val_ toDeleteIndecies) . postId)
223− nIds <- liftIO . P.runBeamPostgres conn . runInsertReturningList (posts socialDb) $ insertExpressions $ take toAdd . flip map (zip rns t) $ \(r, t) -> Post default_ (val_ (userIds %! r)) (val_ t)
225− return (toDeleteIndecies, map postId nIds)
227− liftIO . atomically $ do
228− let upd = Update (S.fromList addedIds) (S.fromList deletedIds)
184+ let upd = Update (S.fromList add) (S.fromList dd)
229− let uppT = updates cfg
185+ let uppT = updates cfg
230− upp <- readTVar uppT
186+ upp <- readTVar uppT
231− let (token, upp') = CB.insert upp upd
187+ let (token, upp') = CB.insert upp upd
232− writeTVar uppT upp'
188+ writeTVar uppT upp'
233− return (token, upd)
189+ return (token, upd)
190+ else return (0, mempty)
235sendUpdate :: Token -> Update -> App ()
192sendUpdate :: Token -> Update -> App ()
236sendUpdate t u = do
193sendUpdate t u = do
237 cfg <- ask
194 cfg <- ask
238 conns <- fmap M.elems . liftIO . readTVarIO $ connections cfg
195 conns <- fmap M.elems . liftIO . readTVarIO $ connections cfg
239− forM conns $ \ conn -> emitTo conn postUpdates $ TokenizedResponse t u
240− -- liftIO $ forM conns $ flip sendTextData $ encode $ TokenizedResponse t u
196+ liftIO $ forM conns $ flip sendTextData $ encode $ TokenizedResponse t u
241 return ()
197 return ()
243−modIndex :: [a] -> Int -> a
244−modIndex aa i = aa !! (i `mod` length aa)
250randomFrom :: Int -> M.Map k v -> k
199randomFrom :: Int -> M.Map k v -> k
251randomFrom g as = M.keys as !! (abs g `mod` M.size as)
200randomFrom g as = M.keys as !! (abs g `mod` M.size as)
253−newUser :: Config -> Text -> Text -> IO [User]
202+newUser :: Config -> Text -> Text -> STM User
254newUser cfg first second = do
203newUser cfg first second = do
255− let conn = dbConn cfg
204+ let lastID = lastUserID cfg
256− P.runBeamPostgres conn . runInsertReturningList (users socialDb) $ insertExpressions [User default_ (val_ first) (val_ second)]
205+ let us = users cfg
258−
207+ id <- readTVar lastID
208+ writeTVar lastID (id+1)
209+ let user = User (UserName first second) id
210+ oldUsers <- readTVar us
211+ writeTVar us $ M.insert id user oldUsers
259−-- newPost :: Config -> UserId -> Text -> STM Post
214+newPost :: Config -> UserId -> Text -> STM Post
260−-- newPost cfg user body = do
215+newPost cfg user body = do
261−-- let lastId = lastPostID cfg
216+ let lastId = lastPostID cfg
262−-- let ps = posts cfg
217+ let ps = posts cfg
264−-- id <- readTVar lastId
219+ id <- readTVar lastId
265−-- writeTVar lastId (id+1)
220+ writeTVar lastId (id+1)
266−-- let p = Post id user body
221+ let p = Post id user body
267−-- modifyTVar ps $ M.insert id p
222+ modifyTVar ps $ M.insert id p
268−-- return p
223+ return p
270−data UserT f = User {
225+data User = User {
226+ userName :: UserName,
271− userId :: Columnar f UserId,
227+ userId :: UserId
272− firstName :: Columnar f Text,
273− secondName :: Columnar f Text
274} deriving Generic
228} deriving Generic
276−instance Beamable UserT
230+data Post = Post {
278−type User = UserT Identity
280−instance Table UserT where
281− data PrimaryKey UserT f = UserId (Columnar f UserId)
284− primaryKey = UserId . userId
286−instance Beamable (PrimaryKey UserT)
288−data PostT f = Post {
289− postId :: Columnar f PostId,
231+ postId :: PostId,
290− postAuthor :: Columnar f UserId,
232+ postAuthor :: UserId,
291− postBody :: Columnar f Text
233+ postBody :: Text
292} deriving Generic
234} deriving Generic
294−instance Beamable PostT
296−type Post = PostT Identity
298instance ToJSON User
236instance ToJSON User
299instance ToJSON Post
237instance ToJSON Post
301−instance Table PostT where
239+data UserName = UserName {
302− data PrimaryKey PostT f = PostId (Columnar f PostId)
240+ firstName :: Text,
303− deriving Generic
242+} deriving (Generic)
305− primaryKey = PostId . postId
307−instance Beamable (PrimaryKey PostT)
244+instance ToJSON UserName
309−data SocialDb f = SocialDb {
310− users :: f (TableEntity UserT),
311− posts :: f (TableEntity PostT)
314−instance Database be SocialDb
316−socialDb :: DatabaseSettings be SocialDb
317−socialDb = defaultDbSettings `withDbModification`
319− users = modifyTable id $ tableModification {
320− firstName = "firstname",
321− secondName = "secondname"
325−subscribeWithToken :: Text
326−subscribeWithToken = "subscribeWithToken"
328−postUpdates = "postUpdates"