Get rid of blob store

This commit is contained in:
Michael Snoyman 2015-05-14 14:33:16 +03:00
parent a53dadcbfc
commit 54645b1eaa
8 changed files with 0 additions and 249 deletions

View File

@ -10,7 +10,6 @@ import qualified Aws
import Control.Concurrent (forkIO, threadDelay) import Control.Concurrent (forkIO, threadDelay)
import Control.Exception (catch) import Control.Exception (catch)
import Control.Monad.Logger (runLoggingT, LoggingT, defaultLogStr) import Control.Monad.Logger (runLoggingT, LoggingT, defaultLogStr)
import Data.BlobStore (fileStore, cachedS3Store)
import Data.WebsiteContent import Data.WebsiteContent
import Data.Streaming.Network (bindPortTCP) import Data.Streaming.Network (bindPortTCP)
import Data.Time (diffUTCTime) import Data.Time (diffUTCTime)
@ -34,7 +33,6 @@ import Yesod.Default.Handlers
import Yesod.Default.Main import Yesod.Default.Main
import Yesod.GitRepo import Yesod.GitRepo
import System.Environment (getEnvironment) import System.Environment (getEnvironment)
import Data.BlobStore (HasBlobStore (..), BlobStore)
import System.IO (hSetBuffering, BufferMode (LineBuffering)) import System.IO (hSetBuffering, BufferMode (LineBuffering))
import qualified Data.ByteString as S import qualified Data.ByteString as S
import qualified Data.Text as T import qualified Data.Text as T
@ -117,18 +115,6 @@ getDbConf conf =
Database.Persist.loadConfig >>= Database.Persist.loadConfig >>=
Database.Persist.applyEnv Database.Persist.applyEnv
loadBlobStore :: Manager -> AppConfig DefaultEnv Extra -> IO (BlobStore StoreKey)
loadBlobStore manager conf =
case storeConfig $ appExtra conf of
BSCFile root -> return $ fileStore root
BSCAWS root access secret bucket prefix -> do
creds <- Aws.Credentials
<$> pure (encodeUtf8 access)
<*> pure (encodeUtf8 secret)
<*> newIORef []
<*> pure Nothing
return $ cachedS3Store root creds bucket prefix manager
-- | Loads up any necessary settings, creates your foundation datatype, and -- | Loads up any necessary settings, creates your foundation datatype, and
-- performs some initialization. -- performs some initialization.
makeFoundation :: Bool -> AppConfig DefaultEnv Extra -> IO App makeFoundation :: Bool -> AppConfig DefaultEnv Extra -> IO App
@ -145,8 +131,6 @@ makeFoundation useEcho conf = do
gen <- MWC.createSystemRandom gen <- MWC.createSystemRandom
blobStore' <- loadBlobStore manager conf
websiteContent' <- if development websiteContent' <- if development
then do then do
void $ rawSystem "git" void $ rawSystem "git"
@ -183,7 +167,6 @@ makeFoundation useEcho conf = do
, persistConfig = dbconf , persistConfig = dbconf
, appLogger = logger , appLogger = logger
, genIO = gen , genIO = gen
, blobStore = blobStore'
, websiteContent = websiteContent' , websiteContent = websiteContent'
, stackageDatabase = stackageDatabase' , stackageDatabase = stackageDatabase'
} }
@ -215,14 +198,11 @@ makeFoundation useEcho conf = do
data CabalLoaderEnv = CabalLoaderEnv data CabalLoaderEnv = CabalLoaderEnv
{ cleSettings :: !(AppConfig DefaultEnv Extra) { cleSettings :: !(AppConfig DefaultEnv Extra)
, cleBlobStore :: !(BlobStore StoreKey)
, cleManager :: !Manager , cleManager :: !Manager
} }
instance HasHackageRoot CabalLoaderEnv where instance HasHackageRoot CabalLoaderEnv where
getHackageRoot = hackageRoot . appExtra . cleSettings getHackageRoot = hackageRoot . appExtra . cleSettings
instance HasBlobStore CabalLoaderEnv StoreKey where
getBlobStore = cleBlobStore
instance HasHttpManager CabalLoaderEnv where instance HasHttpManager CabalLoaderEnv where
getHttpManager = cleManager getHttpManager = cleManager

View File

@ -1,178 +0,0 @@
module Data.BlobStore
( BlobStore (..)
, ToPath (..)
, fileStore
, HasBlobStore (..)
, storeWrite
, storeRead
, storeExists
, BackupToS3 (..)
, cachedS3Store
) where
import ClassyPrelude.Yesod
import qualified Filesystem as F
import Control.Monad.Trans.Resource (release)
import qualified Aws
import Aws.S3 as Aws
import qualified System.IO as IO
import System.Directory (getTemporaryDirectory)
-- FIXME add a sendfile optimization
data BlobStore key = BlobStore
{ storeWrite' :: !(forall m. MonadIO m => key -> Acquire (Sink ByteString m ()))
, storeRead' :: !(forall m. MonadIO m => key -> Acquire (Maybe (Source m ByteString)))
, storeExists' :: !(forall m. MonadIO m => key -> m Bool)
}
class HasBlobStore a key | a -> key where
getBlobStore :: a -> BlobStore key
instance HasBlobStore (BlobStore key) key where
getBlobStore = id
storeWrite :: (MonadResource m, MonadReader env m, HasBlobStore env key)
=> key
-> Consumer ByteString m ()
storeWrite key = do
store <- liftM getBlobStore ask
(releaseKey, sink) <- allocateAcquire $ storeWrite' store key
toConsumer sink
release releaseKey
storeRead :: (MonadResource m, MonadReader env m, HasBlobStore env key)
=> key
-> m (Maybe (Source m ByteString))
storeRead key = do
store <- liftM getBlobStore ask
(releaseKey, msrc) <- allocateAcquire $ storeRead' store key
case msrc of
Nothing -> do
release releaseKey
return Nothing
Just src -> return $ Just $ src >> release releaseKey
storeExists :: (MonadIO m, MonadReader env m, HasBlobStore env key)
=> key
-> m Bool
storeExists key = do
store <- liftM getBlobStore ask
storeExists' store key
class ToPath a where
toPath :: a -> [Text]
fileStore :: ToPath key
=> FilePath -- ^ root
-> BlobStore key
fileStore root = BlobStore
{ storeWrite' = \key -> (sinkHandle . snd) <$> mkAcquireType
(do
let fp = toFP root key
F.createTree $ directory fp
IO.openBinaryTempFile
(fpToString $ directory fp)
(fpToString $ filename fp))
(\(fp, h) rt ->
case rt of
ReleaseException -> do
hClose h `finally` F.removeFile (fpFromString fp)
_ -> do
hClose h
F.rename (fpFromString fp) (toFP root key))
, storeRead' = \key -> (fmap sourceHandle) <$> mkAcquire
((Just <$> F.openFile (toFP root key) F.ReadMode)
`catch` \e ->
if isDoesNotExistError e
then return Nothing
else throwIO e)
(maybe (return ()) hClose)
, storeExists' = liftIO . F.isFile . toFP root
}
toFP :: ToPath a => FilePath -> a -> FilePath
toFP root key = foldl' (\x y -> x </> fpFromText y) root (toPath key)
-- | Note: Only use with data which will never be modified!
cachedS3Store :: (BackupToS3 key, ToPath key)
=> FilePath -- ^ cache directory
-> Aws.Credentials
-> Text -- bucket FIXME Aws.Bucket
-> Text -- ^ prefix within bucket
-> Manager
-> BlobStore key
cachedS3Store cache creds bucket prefix manager =
self
where
self = BlobStore
{ storeWrite' = \key ->
if shouldBackup key
then do
tempDir <- liftIO getTemporaryDirectory
(fp, h) <- mkAcquire
(IO.openBinaryTempFile tempDir "store-write-cache")
(\(fp, h) -> hClose h >> F.removeFile (fpFromString fp))
return $ do
len <- getZipSink $ ZipSink (sinkHandle h) *> ZipSink lengthCE
liftIO $ hClose h
liftIO $ IO.withFile fp IO.ReadMode $ \inH -> runResourceT $ do
-- FIXME the need for this separate manager
-- indicates a serious bug in either aws or (more
-- likely) http-client, must investigate!
manager' <- newManager
res <- Aws.aws
(Aws.Configuration Aws.Timestamp creds
$ Aws.defaultLog Aws.Error)
Aws.defServiceConfig
manager'
(Aws.putObject bucket (toS3Path key)
$ requestBodySource len
$ sourceHandle inH)
void $ Aws.readResponseIO res
liftIO $ IO.withFile fp IO.ReadMode $ \inH -> withAcquire
(storeWrite' (fileStore cache) key)
(sourceHandle inH $$)
else storeWrite' (fileStore cache) key
, storeRead' = \key ->
if shouldBackup key
then do
msrc <- storeRead' (fileStore cache) key
case msrc of
Just src -> return $ Just src
Nothing -> do
join $ liftIO $ handle (\S3Error{} -> return $ return Nothing) $ runResourceT $ do
res <- Aws.aws
(Aws.Configuration Aws.Timestamp creds
$ Aws.defaultLog Aws.Error)
Aws.defServiceConfig
manager
(Aws.getObject bucket (toS3Path key))
gor <- Aws.readResponseIO res
let fp = toFP cache key
liftIO $ F.createTree $ directory fp
bracketOnError
(liftIO $ IO.openBinaryTempFile
(fpToString $ directory fp)
(fpToString $ filename fp))
(\(fpTmp, h) -> liftIO $ do
hClose h
F.removeFile (fpFromString fpTmp))
$ \(fpTmp, h) -> do
responseBody (Aws.gorResponse gor) $$+- sinkHandle h
liftIO $ do
hClose h
F.rename (fpFromString fpTmp) fp
return $ storeRead' (fileStore cache) key -- FIXME optimize?
else storeRead' (fileStore cache) key
, storeExists' = \key ->
if shouldBackup key
then liftIO $ withAcquire (storeRead' self key)
$ \msrc -> return
$ maybe False (const True)
(msrc :: Maybe (Source IO ByteString))
else storeExists' (fileStore cache) key
}
toS3Path key = intercalate "/" $ filter (not . null) $ prefix : toPath key
class BackupToS3 key where
shouldBackup :: key -> Bool

View File

@ -1,7 +1,6 @@
module Foundation where module Foundation where
import ClassyPrelude.Yesod import ClassyPrelude.Yesod
import Data.BlobStore
import Data.Slug (safeMakeSlug, HasGenIO (getGenIO), randomSlug, Slug) import Data.Slug (safeMakeSlug, HasGenIO (getGenIO), randomSlug, Slug)
import Data.WebsiteContent import Data.WebsiteContent
import qualified Database.Persist import qualified Database.Persist
@ -35,14 +34,10 @@ data App = App
, persistConfig :: Settings.PersistConf , persistConfig :: Settings.PersistConf
, appLogger :: Logger , appLogger :: Logger
, genIO :: MWC.GenIO , genIO :: MWC.GenIO
, blobStore :: BlobStore StoreKey
, websiteContent :: GitRepo WebsiteContent , websiteContent :: GitRepo WebsiteContent
, stackageDatabase :: StackageDatabase , stackageDatabase :: StackageDatabase
} }
instance HasBlobStore App StoreKey where
getBlobStore = blobStore
instance HasGenIO App where instance HasGenIO App where
getGenIO = genIO getGenIO = genIO

View File

@ -6,7 +6,6 @@ import Control.Concurrent (forkIO)
import Crypto.Hash (Digest, SHA1) import Crypto.Hash (Digest, SHA1)
import Crypto.Hash.Conduit (sinkHash) import Crypto.Hash.Conduit (sinkHash)
import Data.Aeson (withObject) import Data.Aeson (withObject)
import Data.BlobStore
import qualified Data.ByteString.Base16 as B16 import qualified Data.ByteString.Base16 as B16
import Data.Byteable (toBytes) import Data.Byteable (toBytes)
import Data.Conduit.Zlib (gzip) import Data.Conduit.Zlib (gzip)

View File

@ -1,7 +1,6 @@
module Handler.StackageIndex where module Handler.StackageIndex where
import Import import Import
import Data.BlobStore
import Stackage.Database import Stackage.Database
getStackageIndexR :: SnapName -> Handler TypedContent getStackageIndexR :: SnapName -> Handler TypedContent

View File

@ -3,7 +3,6 @@ module Handler.StackageSdist
) where ) where
import Import import Import
import Data.BlobStore
import Stackage.Database import Stackage.Database
import Handler.Package (packagePage) import Handler.Package (packagePage)

View File

@ -2,7 +2,6 @@ module Types where
import ClassyPrelude.Yesod import ClassyPrelude.Yesod
import Data.Aeson import Data.Aeson
import Data.BlobStore (ToPath (..), BackupToS3 (..))
import Data.Hashable (hashUsing) import Data.Hashable (hashUsing)
import Text.Blaze (ToMarkup) import Text.Blaze (ToMarkup)
import Database.Persist.Sql (PersistFieldSql (sqlType)) import Database.Persist.Sql (PersistFieldSql (sqlType))
@ -60,52 +59,11 @@ instance PathPiece PackageNameVersion where
where where
f c = (c == '.') || ('0' <= c && c <= '9') f c = (c == '.') || ('0' <= c && c <= '9')
data StoreKey = HackageCabal !PackageName !Version
| HackageSdist !PackageName !Version
| CabalIndex !PackageSetIdent
| CustomSdist !PackageSetIdent !PackageName !Version
| SnapshotBundle !PackageSetIdent
| HaddockBundle !PackageSetIdent
| HoogleDB !PackageSetIdent !HoogleVersion
deriving (Show, Eq, Ord, Typeable)
newtype HoogleVersion = HoogleVersion Text newtype HoogleVersion = HoogleVersion Text
deriving (Show, Eq, Ord, Typeable, PathPiece) deriving (Show, Eq, Ord, Typeable, PathPiece)
currentHoogleVersion :: HoogleVersion currentHoogleVersion :: HoogleVersion
currentHoogleVersion = HoogleVersion VERSION_hoogle currentHoogleVersion = HoogleVersion VERSION_hoogle
instance ToPath StoreKey where
toPath (HackageCabal name version) = ["hackage", toPathPiece name, toPathPiece version ++ ".cabal"]
toPath (HackageSdist name version) = ["hackage", toPathPiece name, toPathPiece version ++ ".tar.gz"]
toPath (CabalIndex ident) = ["cabal-index", toPathPiece ident ++ ".tar.gz"]
toPath (CustomSdist ident name version) =
[ "custom-tarball"
, toPathPiece ident
, toPathPiece name
, toPathPiece version ++ ".tar.gz"
]
toPath (SnapshotBundle ident) =
[ "bundle"
, toPathPiece ident ++ ".tar.gz"
]
toPath (HaddockBundle ident) =
[ "haddock"
, toPathPiece ident ++ ".tar.xz"
]
toPath (HoogleDB ident ver) =
[ "hoogle"
, toPathPiece ver
, toPathPiece ident ++ ".hoo.gz"
]
instance BackupToS3 StoreKey where
shouldBackup HackageCabal{} = False
shouldBackup HackageSdist{} = False
shouldBackup CabalIndex{} = True
shouldBackup CustomSdist{} = True
shouldBackup SnapshotBundle{} = True
shouldBackup HaddockBundle{} = True
shouldBackup HoogleDB{} = True
newtype HackageRoot = HackageRoot { unHackageRoot :: Text } newtype HackageRoot = HackageRoot { unHackageRoot :: Text }
deriving (Show, Read, Typeable, Eq, Ord, Hashable, PathPiece, ToMarkup) deriving (Show, Read, Typeable, Eq, Ord, Hashable, PathPiece, ToMarkup)

View File

@ -22,7 +22,6 @@ library
Settings.Development Settings.Development
Data.Slug Data.Slug
Data.Tag Data.Tag
Data.BlobStore
Data.GhcLinks Data.GhcLinks
Data.WebsiteContent Data.WebsiteContent
Types Types