1
1
module Hasura.Server.SchemaUpdate
2
- ( ThreadType (.. )
3
- , schemaUpdateEventListener
4
- , schemaUpdateEventProcessor
5
- )
2
+ (startSchemaSync )
6
3
where
7
4
8
5
import Hasura.Prelude
@@ -12,13 +9,15 @@ import Hasura.RQL.DDL.Schema.Table (buildSchemaCache)
12
9
import Hasura.RQL.Types
13
10
import Hasura.Server.App (SchemaCacheRef (.. ), withSCUpdate )
14
11
import Hasura.Server.Init (InstanceId (.. ))
12
+ import Hasura.Server.Logging
15
13
import Hasura.Server.Query
16
14
17
15
import Data.Aeson
18
16
import Data.Aeson.Casing
19
17
import Data.Aeson.TH
20
- import Data.UUID
21
18
19
+ import qualified Control.Concurrent as C
20
+ import qualified Control.Concurrent.Async as A
22
21
import qualified Control.Concurrent.STM as STM
23
22
import qualified Data.Text as T
24
23
import qualified Data.Time as UTC
@@ -58,20 +57,11 @@ instance ToEngineLog SchemaUpdateEventLog where
58
57
59
58
data EventPayload
60
59
= EventPayload
61
- { _epInstanceId :: ! UUID
60
+ { _epInstanceId :: ! InstanceId
62
61
, _epOccurredAt :: ! UTC. UTCTime
63
62
} deriving (Show , Eq )
64
63
$ (deriveJSON (aesonDrop 3 snakeCase) ''EventPayload)
65
64
66
- data SchemaUpdateEvent
67
- = SUESuccess ! EventPayload
68
- | SUEPGReConn
69
- deriving (Show , Eq )
70
-
71
- instance ToJSON SchemaUpdateEvent where
72
- toJSON (SUESuccess payload) = toJSON payload
73
- toJSON SUEPGReConn = String " postgres reconnection"
74
-
75
65
data ThreadError
76
66
= TEJsonParse ! T. Text
77
67
| TEQueryError ! QErr
@@ -81,83 +71,147 @@ $(deriveToJSON
81
71
}
82
72
''ThreadError)
83
73
74
+ -- | An IO action that enables metadata syncing
75
+ startSchemaSync
76
+ :: Bool
77
+ -> PG. PGPool
78
+ -> Logger
79
+ -> HTTP. Manager
80
+ -> SchemaCacheRef
81
+ -> InstanceId
82
+ -> Maybe UTC. UTCTime -> IO ()
83
+ startSchemaSync strfyNum pool logger httpMgr cacheRef instanceId cacheInitTime = do
84
+ -- Init events queue
85
+ eventsQueue <- STM. newTQueueIO
86
+ -- Start listener thread
87
+ lAsync <- A. async $ listener strfyNum pool
88
+ logger httpMgr eventsQueue cacheRef instanceId cacheInitTime
89
+
90
+ -- Start processor thread
91
+ pAsync <- A. async $ processor strfyNum pool
92
+ logger httpMgr eventsQueue cacheRef instanceId
93
+
94
+ void $ A. waitAny [lAsync, pAsync]
95
+
84
96
-- | An IO action that listens to postgres for events and pushes them to a Queue
85
- schemaUpdateEventListener
86
- :: PG. PGPool
97
+ listener
98
+ :: Bool
99
+ -> PG. PGPool
87
100
-> Logger
88
- -> STM. TQueue SchemaUpdateEvent
89
- -> IO ()
90
- schemaUpdateEventListener pool logger eventsQueue =
101
+ -> HTTP. Manager
102
+ -> STM. TQueue EventPayload
103
+ -> SchemaCacheRef
104
+ -> InstanceId
105
+ -> Maybe UTC. UTCTime -> IO ()
106
+ listener strfyNum pool logger httpMgr eventsQueue
107
+ cacheRef instanceId cacheInitTime = do
108
+ logThreadStartup logger instanceId threadType
91
109
-- Never exits
92
110
forever $ do
93
- listenResE <- liftIO $ runExceptT $ PG. listen pool pgChannel notifyHandler
111
+ listenResE <-
112
+ liftIO $ runExceptT $ PG. listen pool pgChannel notifyHandler
94
113
either onError return listenResE
114
+ logWarn
115
+ C. threadDelay $ 1 * 1000 * 1000 -- 1 second
95
116
where
96
- notifyHandler = PG. NotifyHandler onReconn onMessage
97
117
threadType = TTListener
98
118
119
+ shouldRefresh dbInstId accrdAt =
120
+ case cacheInitTime of
121
+ Nothing -> True
122
+ Just time -> (dbInstId /= instanceId) && accrdAt > time
123
+
124
+ refreshCache Nothing = return ()
125
+ refreshCache (Just (dbInstId, accrdAt)) =
126
+ when (shouldRefresh dbInstId accrdAt) $
127
+ refreshSchemaCache strfyNum pool logger httpMgr cacheRef
128
+ threadType " reloading schema cache on listen start"
129
+
130
+ notifyHandler = \ case
131
+ PG. PNEOnStart -> do
132
+ eRes <- runExceptT $ PG. runTx pool
133
+ (PG. Serializable , Nothing ) fetchLastUpdate
134
+ case eRes of
135
+ Left e -> onError e
136
+ Right mLastUpd -> refreshCache mLastUpd
137
+
138
+ PG. PNEPQNotify notif ->
139
+ case eitherDecodeStrict $ PQ. notifyExtra notif of
140
+ Left e -> logError logger threadType $ TEJsonParse $ T. pack e
141
+ Right payload -> do
142
+ logInfo logger threadType $ object [" received_event" .= payload]
143
+ -- Push a notify event to Queue
144
+ STM. atomically $ STM. writeTQueue eventsQueue payload
145
+
99
146
onError = logError logger threadType . TEQueryError
147
+ logWarn = unLogger logger $
148
+ SchemaUpdateEventLog LevelWarn TTListener $ String
149
+ " error occured retrying pg listen after 1 second"
100
150
101
- onReconn = do
102
- -- emit postgres reconnection event
103
- let event = SUEPGReConn
104
- logInfo logger threadType $ object [" received_event" .= event]
105
- STM. atomically $ STM. writeTQueue eventsQueue event
106
-
107
- -- Postgres notification handler
108
- onMessage notif =
109
- case eitherDecodeStrict $ PQ. notifyExtra notif of
110
- Left e -> logError logger threadType $ TEJsonParse $ T. pack e
111
- Right payload -> do
112
- let newEvent = SUESuccess payload
113
- logInfo logger threadType $ object [" received_event" .= newEvent]
114
- -- Push a success event to Queue along with event payload
115
- STM. atomically $ STM. writeTQueue eventsQueue newEvent
116
151
117
152
-- | An IO action that processes events from Queue
118
- schemaUpdateEventProcessor
153
+ processor
119
154
:: Bool
120
155
-> PG. PGPool
121
156
-> Logger
122
157
-> HTTP. Manager
123
- -> STM. TQueue SchemaUpdateEvent
158
+ -> STM. TQueue EventPayload
124
159
-> SchemaCacheRef
125
- -> InstanceId
126
- -> Maybe UTC. UTCTime
127
- -> IO ()
128
- schemaUpdateEventProcessor strfyNum pool logger httpManager
129
- eventsQueue cacheRef instanceId cacheInit =
160
+ -> InstanceId -> IO ()
161
+ processor strfyNum pool logger httpMgr eventsQueue
162
+ cacheRef instanceId = do
163
+ logThreadStartup logger instanceId threadType
130
164
-- Never exits
131
165
forever $ do
132
166
event <- STM. atomically $ STM. readTQueue eventsQueue
133
167
logInfo logger threadType $ object [" processed_event" .= event]
134
- when (shouldReload event) $ do
135
- -- Reload schema cache from catalog
136
- resE <- liftIO $ runExceptT $ withSCUpdate cacheRef $
137
- peelRun emptySchemaCache adminUserInfo
138
- httpManager strfyNum pool PG. Serializable buildSchemaCache
139
- case resE of
140
- Left e -> logError logger threadType $ TEQueryError e
141
- Right _ ->
142
- logInfo logger threadType $
143
- object [" message" .= (" schema cache reloaded" :: T. Text )]
168
+ when (shouldReload event) $
169
+ refreshSchemaCache strfyNum pool logger httpMgr cacheRef
170
+ threadType " schema cache reloaded"
144
171
where
145
172
threadType = TTProcessor
146
173
147
- -- If postgres reconnect happens reload schema cache
148
- shouldReload SUEPGReConn = True
149
- -- If event is from another server and occurred after
150
- -- init schema cache built then reload
151
- shouldReload (SUESuccess payload) =
152
- (_epInstanceId payload /= getInstanceId instanceId)
153
- && maybe True (withCacheInit $ _epOccurredAt payload) cacheInit
174
+ -- If event is from another server
175
+ shouldReload payload = _epInstanceId payload /= instanceId
154
176
155
- withCacheInit occurredAt initTime = occurredAt > initTime
177
+ logThreadStartup
178
+ :: Show a
179
+ => Logger
180
+ -> InstanceId
181
+ -> a -> IO ()
182
+ logThreadStartup logger instanceId threadType =
183
+ unLogger logger threadLog
184
+ where
185
+ threadLog =
186
+ let msg = T. pack (show threadType) <> " thread started"
187
+ in StartupLog LevelInfo " threads" $
188
+ object [ " instance_id" .= getInstanceId instanceId
189
+ , " message" .= msg
190
+ ]
191
+
192
+ refreshSchemaCache
193
+ :: Bool
194
+ -> PG. PGPool
195
+ -> Logger
196
+ -> HTTP. Manager
197
+ -> SchemaCacheRef
198
+ -> ThreadType
199
+ -> T. Text -> IO ()
200
+ refreshSchemaCache strfyNum pool logger httpManager cacheRef threadType msg = do
201
+ -- Reload schema cache from catalog
202
+ resE <- liftIO $ runExceptT $ withSCUpdate cacheRef $
203
+ peelRun emptySchemaCache adminUserInfo
204
+ httpManager strfyNum pool PG. Serializable buildSchemaCache
205
+ case resE of
206
+ Left e -> logError logger threadType $ TEQueryError e
207
+ Right _ ->
208
+ logInfo logger threadType $ object [" message" .= msg]
156
209
157
210
logInfo :: Logger -> ThreadType -> Value -> IO ()
158
211
logInfo logger threadType val = unLogger logger $
159
212
SchemaUpdateEventLog LevelInfo threadType val
160
213
161
214
logError :: ToJSON a => Logger -> ThreadType -> a -> IO ()
162
- logError logger threadType err = unLogger logger $
163
- SchemaUpdateEventLog LevelError threadType $ object [" error" .= toJSON err]
215
+ logError logger threadType err =
216
+ unLogger logger $ SchemaUpdateEventLog LevelError threadType $
217
+ object [" error" .= toJSON err]
0 commit comments