commit - 0cab7a6b2a82d6a3d7694db9a63775bc9712b689
commit + 734014cedecabe12fecb96a37fc38cbf0fd072c1
blob - a046c5b38ceac71fde10ad03e5e7067730439258
blob + 9f8d7db2bb5428180b5d89637e21b7ae4c035982
--- src/Main.purs
+++ src/Main.purs
, displayName :: String
, enrichMetadataFiber :: Maybe (Fiber Unit)
, backupFiber :: Maybe (Fiber Unit)
+ , syncFibers :: Array (Fiber Unit)
}
normalizePath :: String -> String
Just lfmUser, Just apiKey -> Just <$> forkAff (lfSync conn apiKey lfmUser slug writeLock)
_, _ -> pure Nothing
+ -- Join the initial sync fibers so their completion is logged before loops start.
void $ forkAff do
for_ lbFiber joinFiber
for_ lfFiber joinFiber
- for_ config.listenbrainzUser \username ->
- void $ forkAff $ lbSyncLoop conn username slug writeLock
- case config.lastfmUser, config.lastfmApiKey of
- Just lfmUser, Just apiKey -> void $ forkAff $ lfSyncLoop conn apiKey lfmUser slug writeLock
- _, _ -> pure unit
+ -- Spawn the recurring sync loops eagerly; each starts with a 60s delay so
+ -- they won't race with the initial sync above. Track them so cleanupUser
+ -- can kill them on shutdown.
+ loopFibers <- traverse forkAff
+ $
+ [ case config.listenbrainzUser of
+ Just username -> Just $ lbSyncLoop conn username slug writeLock
+ Nothing -> Nothing
+ , case config.lastfmUser, config.lastfmApiKey of
+ Just lfmUser, Just apiKey -> Just $ lfSyncLoop conn apiKey lfmUser slug writeLock
+ _, _ -> Nothing
+ ] # Data.Array.mapMaybe identity
+
enrichMetadataFiber <- forkAff $ enrichMetadata conn config slug
backupFiber <-
if config.backupEnabled then Just <$> forkAff (backupDb conn config.databaseFile (s3ConfigFromUser config) (Int.toNumber config.backupIntervalHours * 3600000.0) slug)
else pure Nothing
let displayName = fromMaybe (if slug == "" then "root" else slug) name
- pure { conn, writeLock, config, slug, displayName, enrichMetadataFiber: Just enrichMetadataFiber, backupFiber }
+ pure { conn, writeLock, config, slug, displayName, enrichMetadataFiber: Just enrichMetadataFiber, backupFiber, syncFibers: loopFibers }
cleanupUser :: UserContext -> Aff Unit
cleanupUser ctx = do
void $ try $ killFiber (Exception.error "Server shutting down") fiber
for_ ctx.backupFiber \fiber ->
void $ try $ killFiber (Exception.error "Server shutting down") fiber
+ for_ ctx.syncFibers \fiber ->
+ void $ try $ killFiber (Exception.error "Server shutting down") fiber
Log.info $ "Closed user: " <> label
foreign import dotenvConfig :: Effect Unit
blob - 784967cb6fcddfccd29f459da314d8089529f650
blob + 8e8fad7726de0b1cc40222bf74c0dba363919439
--- test/Main.purs
+++ test/Main.purs
import Prelude
-import Unsafe.Coerce (unsafeCoerce)
import Data.Argonaut (decodeJson, encodeJson, parseJson)
import Data.Array (length)
import Data.Either (Either(..), isRight)
import Data.Maybe (Maybe(..))
import Effect (Effect)
+import Effect.Aff.AVar as Avar
import Test.Spec (describe, it)
import Test.Spec.Assertions (shouldEqual, fail)
import Data.String.Regex (regex, parseFlags)
, backupEnabled: false
, backupIntervalHours: 0
}
- ctx1 = { conn: conn1, writeLock: unsafeCoerce unit, config: dummyConfig, slug: "user1", displayName: "User 1", enrichMetadataFiber: Nothing, backupFiber: Nothing }
- ctx2 = { conn: conn2, writeLock: unsafeCoerce unit, config: dummyConfig, slug: "user2", displayName: "User 2", enrichMetadataFiber: Nothing, backupFiber: Nothing }
+ lock1 <- Avar.new unit
+ lock2 <- Avar.new unit
+ let
+ ctx1 = { conn: conn1, writeLock: lock1, config: dummyConfig, slug: "user1", displayName: "User 1", enrichMetadataFiber: Nothing, backupFiber: Nothing, syncFibers: [] }
+ ctx2 = { conn: conn2, writeLock: lock2, config: dummyConfig, slug: "user2", displayName: "User 2", enrichMetadataFiber: Nothing, backupFiber: Nothing, syncFibers: [] }
contexts = [ ctx1, ctx2 ]
res1 <- findUserByToken contexts token1