In memory upload history database

This commit is contained in:
Michael Snoyman 2014-04-16 15:29:24 +03:00
parent 5c8229ac03
commit b2170578ae
3 changed files with 73 additions and 35 deletions

View File

@ -15,8 +15,8 @@ import Network.Wai.Middleware.RequestLogger
) )
import qualified Network.Wai.Middleware.RequestLogger as RequestLogger import qualified Network.Wai.Middleware.RequestLogger as RequestLogger
import qualified Database.Persist import qualified Database.Persist
import Control.Monad.Logger (runLoggingT) import Control.Monad.Logger (runLoggingT, LoggingT)
import Control.Monad.Reader (runReaderT) import Control.Monad.Reader (runReaderT, ReaderT)
import Control.Concurrent (forkIO, threadDelay) import Control.Concurrent (forkIO, threadDelay)
import System.Log.FastLogger (newStdoutLoggerSet, defaultBufSize, flushLogStr) import System.Log.FastLogger (newStdoutLoggerSet, defaultBufSize, flushLogStr)
import Network.Wai.Logger (clockDateCacher) import Network.Wai.Logger (clockDateCacher)
@ -120,18 +120,16 @@ makeFoundation conf = do
-- Start the cabal file loader -- Start the cabal file loader
void $ forkIO $ forever $ flip runLoggingT (messageLoggerSource foundation logger) $ do void $ forkIO $ forever $ flip runLoggingT (messageLoggerSource foundation logger) $ do
when development $ liftIO $ threadDelay $ 5 * 60 * 1000000 --when development $ liftIO $ threadDelay $ 5 * 60 * 1000000
eres <- tryAny $ flip runReaderT foundation $ do eres <- tryAny $ flip runReaderT foundation $ do
loadCabalFiles $ \name version mmtime -> let runDB' :: SqlPersistT (ResourceT (ReaderT App (LoggingT IO))) a
runResourceT $ flip (Database.Persist.runPool dbconf) p $ do -> ReaderT App (LoggingT IO) a
mx <- getBy $ UniqueUploaded name version runDB' = runResourceT . flip (Database.Persist.runPool dbconf) p
case mx of uploadHistory0 <- runDB' $ selectSource [] [] $$ sinkUploadHistory
Just {} -> return () UploadState uploadHistory newUploads <- loadCabalFiles uploadHistory0
Nothing -> do runDB' $ mapM_ insert newUploads
mtime <- lift $ lift mmtime
forM_ mtime $ void . insertBy . Uploaded name version
let views = let views =
[ ("pvp", viewPVP) [ ("pvp", viewPVP uploadHistory)
, ("no-bounds", viewNoBounds) , ("no-bounds", viewNoBounds)
, ("unchanged", viewUnchanged) , ("unchanged", viewUnchanged)
] ]
@ -139,7 +137,7 @@ makeFoundation conf = do
runResourceT $ flip (Database.Persist.runPool dbconf) p $ createView runResourceT $ flip (Database.Persist.runPool dbconf) p $ createView
name name
func func
(selectSource [] []) (sourceHistory uploadHistory)
(storeWrite $ HackageViewIndex name) (storeWrite $ HackageViewIndex name)
case eres of case eres of
Left e -> $logError $ tshow e Left e -> $logError $ tshow e

View File

@ -3,6 +3,10 @@ module Data.Hackage
, sourceHackageSdist , sourceHackageSdist
, createView , createView
, sourceHackageViewSdist , sourceHackageViewSdist
, sinkUploadHistory
, UploadState (..)
, UploadHistory
, sourceHistory
) where ) where
import ClassyPrelude.Yesod hiding (get) import ClassyPrelude.Yesod hiding (get)
@ -26,9 +30,18 @@ import Distribution.PackageDescription.Parse (parsePackageDescription, ParseResu
import Distribution.PackageDescription.PrettyPrint (showGenericPackageDescription) import Distribution.PackageDescription.PrettyPrint (showGenericPackageDescription)
import Distribution.PackageDescription (GenericPackageDescription, PackageDescription, packageDescription) import Distribution.PackageDescription (GenericPackageDescription, PackageDescription, packageDescription)
import Control.Exception (throw) import Control.Exception (throw)
import Control.Monad.State (modify, put, get) import Control.Monad.State.Strict (modify, put, get, execStateT, MonadState)
import Control.Concurrent.Lifted (fork) import Control.Concurrent.Lifted (fork)
sinkUploadHistory :: Monad m => Consumer (Entity Uploaded) m UploadHistory
sinkUploadHistory =
foldlC go mempty
where
go history (Entity _ (Uploaded name version time)) =
case lookup name history of
Nothing -> insertMap name (singletonMap version time) history
Just vhistory -> insertMap name (insertMap version time vhistory) history
loadCabalFiles :: ( MonadActive m loadCabalFiles :: ( MonadActive m
, MonadBaseControl IO m , MonadBaseControl IO m
, MonadThrow m , MonadThrow m
@ -40,9 +53,9 @@ loadCabalFiles :: ( MonadActive m
, MonadLogger m , MonadLogger m
, MonadCatch m , MonadCatch m
) )
=> (PackageName -> Version -> m (Maybe UTCTime) -> m ()) -- ^ add upload => UploadHistory -- ^ initial
-> m () -> m UploadState
loadCabalFiles addUpload = do loadCabalFiles uploadHistory0 = flip execStateT (UploadState uploadHistory0 []) $ do
HackageRoot root <- liftM getHackageRoot ask HackageRoot root <- liftM getHackageRoot ask
$logDebug $ "Entering loadCabalFiles, root == " ++ root $logDebug $ "Entering loadCabalFiles, root == " ++ root
req <- parseUrl $ unpack $ root ++ "/00-index.tar.gz" req <- parseUrl $ unpack $ root ++ "/00-index.tar.gz"
@ -65,30 +78,46 @@ loadCabalFiles addUpload = do
store <- liftM getBlobStore ask store <- liftM getBlobStore ask
unless exists $ withAcquire (storeWrite' store key) $ \sink -> unless exists $ withAcquire (storeWrite' store key) $ \sink ->
sourceLazy lbs $$ sink sourceLazy lbs $$ sink
setUploadDate name version addUpload setUploadDate name version
_ -> return () _ -> return ()
tarSource Tar.Done = return () tarSource Tar.Done = return ()
tarSource (Tar.Fail e) = throwM e tarSource (Tar.Fail e) = throwM e
tarSource (Tar.Next e es) = yield e >> tarSource es tarSource (Tar.Next e es) = yield e >> tarSource es
type UploadHistory = HashMap PackageName (HashMap Version UTCTime)
data UploadState = UploadState
{ usHistory :: !UploadHistory
, usChanges :: ![Uploaded]
}
setUploadDate :: ( MonadBaseControl IO m setUploadDate :: ( MonadBaseControl IO m
, MonadThrow m , MonadThrow m
, MonadIO m , MonadIO m
, MonadReader env m , MonadReader env m
, MonadState UploadState m
, HasHttpManager env , HasHttpManager env
, MonadLogger m , MonadLogger m
) )
=> PackageName => PackageName
-> Version -> Version
-> (PackageName -> Version -> m (Maybe UTCTime) -> m ())
-> m () -> m ()
setUploadDate name version addUpload = addUpload name version $ do setUploadDate name version = do
req <- parseUrl url UploadState history changes <- get
$logDebug $ "Requesting: " ++ tshow req case lookup name history >>= lookup version of
lbs <- withResponse req $ \res -> responseBody res $$ sinkLazy Just _ -> return ()
let uploadDateT = decodeUtf8 $ toStrict lbs Nothing -> do
return $ parseTime defaultTimeLocale "%c" $ unpack uploadDateT req <- parseUrl url
$logDebug $ "Requesting: " ++ tshow req
lbs <- withResponse req $ \res -> responseBody res $$ sinkLazy
let uploadDateT = decodeUtf8 $ toStrict lbs
case parseTime defaultTimeLocale "%c" $ unpack uploadDateT of
Nothing -> return ()
Just time -> do
let vhistory = insertMap version time $ fromMaybe mempty $ lookup name history
history' = insertMap name vhistory history
changes' = Uploaded name version time : changes
put $ UploadState history' changes'
where where
url = unpack $ concat url = unpack $ concat
[ "http://hackage.haskell.org/package/" [ "http://hackage.haskell.org/package/"
@ -215,7 +244,7 @@ createView :: ( MonadResource m
) )
=> HackageView => HackageView
-> (PackageName -> Version -> UTCTime -> GenericPackageDescription -> m GenericPackageDescription) -> (PackageName -> Version -> UTCTime -> GenericPackageDescription -> m GenericPackageDescription)
-> Source m (Entity Uploaded) -> Source m Uploaded
-> Sink ByteString m () -> Sink ByteString m ()
-> m () -> m ()
createView viewName modifyCabal src sink = withSystemTempDirectory "createview" $ \dir -> do createView viewName modifyCabal src sink = withSystemTempDirectory "createview" $ \dir -> do
@ -224,7 +253,7 @@ createView viewName modifyCabal src sink = withSystemTempDirectory "createview"
entries <- liftIO $ Tar.pack dir (map fpToString $ setToList rels) entries <- liftIO $ Tar.pack dir (map fpToString $ setToList rels)
sourceLazy (Tar.write entries) $$ gzip =$ sink sourceLazy (Tar.write entries) $$ gzip =$ sink
where where
uploadedConduit dir (Entity _ (Uploaded name version time)) = do uploadedConduit dir (Uploaded name version time) = do
let relfp = fpFromText (toPathPiece name) let relfp = fpFromText (toPathPiece name)
</> fpFromText (toPathPiece version) </> fpFromText (toPathPiece version)
</> fpFromText (concat </> fpFromText (concat
@ -250,6 +279,15 @@ createView viewName modifyCabal src sink = withSystemTempDirectory "createview"
writeFile fp new writeFile fp new
return $ asSet $ singletonSet relfp return $ asSet $ singletonSet relfp
sourceHistory :: Monad m => UploadHistory -> Producer m Uploaded
sourceHistory =
mapM_ go . mapToList
where
go (name, vhistory) =
mapM_ go' $ mapToList vhistory
where
go' (version, time) = yield $ Uploaded name version time
-- FIXME put in conduit-combinators -- FIXME put in conduit-combinators
parMapMC _ = mapMC parMapMC _ = mapMC
{- FIXME {- FIXME

View File

@ -9,6 +9,7 @@ import Types hiding (Version (..))
import qualified Types import qualified Types
import Model import Model
import Data.NonNull (fromNullable) -- FIXME expose from ClassyPrelude import Data.NonNull (fromNullable) -- FIXME expose from ClassyPrelude
import Data.Hackage (UploadHistory)
viewUnchanged :: Monad m viewUnchanged :: Monad m
=> packageName -> version -> time => packageName -> version -> time
@ -63,14 +64,15 @@ viewNoBounds _ _ _ =
where where
go (Dependency name _range) = return $ Dependency name anyVersion go (Dependency name _range) = return $ Dependency name anyVersion
viewPVP :: ( Monad m getAvailable name maxUploaded =
, PersistMonadBackend m ~ SqlBackend map fst . filter ((<= maxUploaded) . snd) . mapToList . fromMaybe mempty . lookup name
, PersistQuery m
) viewPVP :: Monad m
=> packageName -> version -> UTCTime => UploadHistory
-> packageName -> version -> UTCTime
-> GenericPackageDescription -> GenericPackageDescription
-> m GenericPackageDescription -> m GenericPackageDescription
viewPVP _ _ uploaded = viewPVP uploadHistory _ _ uploaded =
helper go helper go
where where
wiredIn = asSet $ setFromList $ words "base ghc template-haskell" wiredIn = asSet $ setFromList $ words "base ghc template-haskell"
@ -80,8 +82,8 @@ viewPVP _ _ uploaded =
go (Dependency name _) | toStr name `member` wiredIn = return $ Dependency name anyVersion go (Dependency name _) | toStr name `member` wiredIn = return $ Dependency name anyVersion
go orig@(Dependency _ range) | hasUpperBound range = return orig go orig@(Dependency _ range) | hasUpperBound range = return orig
go orig@(Dependency nameO@(toStr -> name) range) = do go orig@(Dependency nameO@(toStr -> name) range) = do
available <- selectList [UploadedName ==. fromString name, UploadedUploaded <=. uploaded] [] let available = getAvailable (fromString name) uploaded uploadHistory
case fromNullable $ mapMaybe (simpleParse . unpack . toPathPiece . uploadedVersion . entityVal) available of case fromNullable $ mapMaybe (simpleParse . unpack . toPathPiece) available of
Nothing -> return orig Nothing -> return orig
Just vs -> Just vs ->
case pvpBump $ maximum vs of case pvpBump $ maximum vs of