@@ -69,6 +69,7 @@ import qualified Data.Map as M
6969import Control.Distributed.Process.Serializable (Serializable)
7070--import Control.Distributed.Process.Debug
7171import qualified STMContainers.Map as STMMap
72+import qualified STMContainers.Set as STMSet
7273import ProjectM36.Session
7374import ProjectM36.Sessions
7475import ListT
@@ -98,7 +99,9 @@ defaultRemoteConnectionInfo :: ConnectionInfo
9899defaultRemoteConnectionInfo = RemoteProcessConnectionInfo defaultDatabaseName (createNodeId "127.0.0.1" defaultServerPort)
99100
100101-- | The 'Connection' represents either local or remote access to a database. All operations flow through the connection.
101−data Connection = InProcessConnection PersistenceStrategy Sessions (TVar TransactionGraph) |
102+type ClientNodes = STMSet.Set ProcessId
103+
104+data Connection = InProcessConnection PersistenceStrategy ClientNodes Sessions (TVar TransactionGraph) |
102105 RemoteProcessConnection LocalNode ProcessId
103106
104107-- | There are several reasons why a connection can fail.
@@ -130,10 +133,12 @@ connectProjectM36 (InProcessConnectionInfo strat) = do
130133 --create date examples graph for now- probably should be empty context in the future
131134 NoPersistence -> do
132135 graphTvar <- newTVarIO freshGraph
136+ clientNodes <- STMSet.newIO
133137 sessions <- STMMap.newIO
134− return $ Right $ InProcessConnection strat sessions graphTvar
138+ return $ Right $ InProcessConnection strat clientNodes sessions graphTvar
135139 MinimalPersistence dbdir -> connectPersistentProjectM36 strat NoDiskSync dbdir freshGraph
136140 CrashSafePersistence dbdir -> connectPersistentProjectM36 strat FsyncDiskSync dbdir freshGraph
141+
137142
138143connectProjectM36 (RemoteProcessConnectionInfo databaseName serverNodeId) = do
139144 connStatus <- newEmptyMVar
@@ -148,7 +153,7 @@ connectProjectM36 (RemoteProcessConnectionInfo databaseName serverNodeId) = do
148153 case mServerProcessId of
149154 Nothing -> liftIO $ putMVar connStatus $ Left (NoSuchDatabaseByNameError databaseName)
150155 Just serverProcessId -> do
151− loginConfirmation <- call serverProcessId Login
156+ loginConfirmation <- call serverProcessId (Login loginNode)
152157 if not loginConfirmation then
153158 liftIO $ putMVar connStatus (Left LoginError)
154159 else do
@@ -206,7 +211,13 @@ createSessionAtHead headn conn@(InProcessConnection _ _ tvar) = do
206211 case transactionForHead headn graph of
207212 Nothing -> pure $ Left (NoSuchHeadNameError headn)
208213 Just trans -> createSessionAtCommit_ (transactionUUID trans) newSessionId conn
209−createSessionAtHead headn conn@(RemoteProcessConnection _ _) = remoteCall conn (CreateSessionAtHead headn)
214+createSessionAtHead headn conn@(RemoteProcessConnection _ _) = remoteCall conn (CreateSessionAtHead headn)
215+
216+-- | Used internall for server connections to keep track of remote nodes for the purpose of sending notifications later.
217+addClientNode :: Connection -> ProcessId -> IO ()
218+addClientNode (RemoteProcessConnection _ _) = error "addClientNode called on remote connection"
219+addClientNode (InProcessConnection _ clientNodes _ _) newProcessId = atomically (STMSet.insert newProcessId clientNodes)
220+
210221
211222-- | Discards a session, eliminating any uncommitted changes present in the session.
212223closeSession :: SessionId -> Connection -> IO ()
@@ -268,10 +279,8 @@ executeDatabaseContextExpr sessionId (InProcessConnection _ sessions _) expr = a
268279 return Nothing
269280executeDatabaseContextExpr sessionId conn@(RemoteProcessConnection _ _) dbExpr = remoteCall conn (ExecuteDatabaseContextExpr sessionId dbExpr)
270281
271−-- | Execute a transaction graph expression in the context of the session and connection. Transaction graph operators modify the transaction graph state.
272−executeGraphExpr :: SessionId -> Connection -> TransactionGraphOperator -> IO (Maybe RelationalError)
273−executeGraphExpr sessionId (InProcessConnection strat sessions graphTvar) graphExpr = do
274− freshUUID <- nextRandom
282+executeGraphExprSTM_ :: SessionId -> Sessions -> TransactionGraphOperator -> TVar TransactionGraph -> STM (Either RelationalError TransactionGraph)
283+executeGraphExprSTM_ sessionId sessions graphExpr graphTvar = do
275284 manip <- atomically $ do
276285 graph <- readTVar graphTvar
277286 eSession <- sessionForSessionId sessionId sessions
@@ -284,6 +293,12 @@ executeGraphExpr sessionId (InProcessConnection strat sessions graphTvar) graphE
284293 let newSession = Session discon'
285294 STMMap.insert newSession sessionId sessions
286295 pure $ Right graph'
296+
297+-- | Execute a transaction graph expression in the context of the session and connection. Transaction graph operators modify the transaction graph state.
298+executeGraphExpr :: SessionId -> Connection -> TransactionGraphOperator -> IO (Maybe RelationalError)
299+executeGraphExpr sessionId (InProcessConnection strat sessions graphTvar) graphExpr = do
300+ freshUUID <- nextRandom
301+ manip <- atomically $ executeGraphExprSTM_ sessionId sessions graphTvar
287302 case manip of
288303 Left err -> return $ Just err
289304 Right newGraph -> do
@@ -292,10 +307,33 @@ executeGraphExpr sessionId (InProcessConnection strat sessions graphTvar) graphE
292307 processPersistence strat newGraph
293308 return Nothing
294309executeGraphExpr sessionId conn@(RemoteProcessConnection _ _) graphExpr = remoteCall conn (ExecuteGraphExpr sessionId graphExpr)
295−
296−-- | After modifying a session, 'commit' the transaction to the transaction graph at the head which the session is referencing.
310+
311+commitSTM_ :: SessionId -> Sessions -> STM (Maybe RelationalError)
312+commitSTM_ sessionId sessions = executeGraphExprSTM_ sessionId sessions graph Commit
313+
314+-- | After modifying a session, 'commit' the transaction to the transaction graph at the head which the session is referencing. This will also trigger checks for any notifications which need to be propagated.
297315commit :: SessionId -> Connection -> IO (Maybe RelationalError)
298−commit sessionId conn@(InProcessConnection _ _ _) = executeGraphExpr sessionId conn Commit
316+commit sessionId conn@(InProcessConnection _ sessions graphTvar) = do
317+ mNots <- atomically $ do
318+ graph <- readTVar graphTvar
319+ -- take the notifications of the current context and scan them for changes in the previous -> current contexts
320+ eSession <- sessionForSessionId sessionId sessions
321+ case eSession of
322+ Left err -> pure (Left err)
323+ Right (Session (DisconnectedTransaction parentUUID currentContext) -> do
324+ --grab previous context
325+ case transactionForUUID parentUUID graph of
326+ Left err -> pure $ Left err
327+ Right (Transaction _ _ previousContext) -> do
328+ let nots = notifications currentContext
329+ fireNots = notificationChanges nots previousContext currentContext
330+ commitSTM_ sessionId sessions graph
331+ pure (Right fireNots)
332+ case mNots of
333+ Left err -> pure (Just err)
334+ Right notsToFire -> do
335+ --trigger the notifications on all clients
336+
299337commit sessionId conn@(RemoteProcessConnection _ _) = remoteCall conn (ExecuteGraphExpr sessionId Commit)
300338
301339-- | Discard any changes made in the current session. This resets the disconnected transaction to reference the original database context of the parent transaction and is a very cheap operation.
@@ -370,10 +408,8 @@ headTransactionUUID sessionId (InProcessConnection _ sessions _) = do
370408 Right session -> pure $ Just (sessionParentUUID session)
371409headTransactionUUID sessionId conn@(RemoteProcessConnection _ _) = remoteCall conn (RetrieveHeadTransactionUUID sessionId)
372410
373−-- | Returns Just the name of the head of the current disconnected transaction or Nothing.
374−headName :: SessionId -> Connection -> IO (Maybe HeadName)
375−headName sessionId (InProcessConnection _ sessions graphTvar) = do
376− atomically $ do
411+headNameSTM_ :: SessionId -> Sessions -> TransactionGraph -> STM (Maybe HeadName)
412+headNameSTM_ sessionId sessions graphTvar= do
377413 graph <- readTVar graphTvar
378414 eSession <- sessionForSessionId sessionId sessions
379415 case eSession of
@@ -381,6 +417,11 @@ headName sessionId (InProcessConnection _ sessions graphTvar) = do
381417 Right session -> pure $ case transactionForUUID (sessionParentUUID session) graph of
382418 Left _ -> Nothing
383419 Right parentTrans -> headNameForTransaction parentTrans graph
420+
421+-- | Returns Just the name of the head of the current disconnected transaction or Nothing.
422+headName :: SessionId -> Connection -> IO (Maybe HeadName)
423+headName sessionId (InProcessConnection _ sessions graphTvar) = do
424+ atomically (headNameSTM_ sessionId sessions graphTvar)
384425headName sessionId conn@(RemoteProcessConnection _ _) = remoteCall conn (ExecuteHeadName sessionId)
385426
386427