about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <vincent@spotify.com>2014-08-22T13·43+0200
committerVincent Ambo <vincent@spotify.com>2014-08-22T13·44+0200
commit2c0eecc9aa0496318a4ccf6205c32a41aa2b9970 (patch)
treebf4dc1605a67fd660923174aeeb190562ca9e9cb
parent41bee335c83ba7eb2f56241d41ccdd373c9a1a1a (diff)
Add the weird safecopy migration tool
-rw-r--r--tools/acid-fixer/Main.hs228
1 files changed, 228 insertions, 0 deletions
diff --git a/tools/acid-fixer/Main.hs b/tools/acid-fixer/Main.hs
new file mode 100644
index 000000000000..8ef3190cc5e7
--- /dev/null
+++ b/tools/acid-fixer/Main.hs
@@ -0,0 +1,228 @@
+{-# LANGUAGE DeriveDataTypeable         #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE RecordWildCards            #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE TemplateHaskell            #-}
+{-# LANGUAGE TypeFamilies               #-}
+
+module Main where
+
+import           Control.Monad.Reader   (ask)
+import           Control.Monad.State    (get, put)
+import           Data.Acid
+import           Data.Acid.Advanced
+import           Data.Acid.Local
+import           Data.ByteString        (ByteString)
+import           Data.Char              (toLower)
+import           Data.Data              (Data, Typeable)
+import           Data.IxSet             (Indexable (..), IxSet (..), Proxy (..),
+                                         getOne, ixFun, ixSet, (@=))
+import           Data.List              (insert)
+import           Data.SafeCopy          
+import           Data.Text              (Text, pack)
+import           Data.Text.Lazy         (toStrict)
+import           Data.Time
+import           Happstack.Server       (FromReqURI (..))
+import           System.Environment     (getEnv)
+
+import qualified Crypto.Hash.SHA512     as SHA (hash)
+import qualified Data.ByteString.Base64 as B64 (encode)
+import qualified Data.ByteString.Char8  as B
+import qualified Data.IxSet             as IxSet
+import qualified Data.Text              as Text
+             
+newtype EntryId = EntryId { unEntryId :: Integer }
+    deriving (Eq, Ord, Data, Enum, Typeable)
+
+$(deriveSafeCopy 2 'base ''EntryId)
+  
+instance Show EntryId where
+  show = show . unEntryId
+
+data BlogLang = EN | DE
+    deriving (Eq, Ord, Data, Typeable)
+
+instance Show BlogLang where
+    show DE = "de"
+    show EN = "en"
+
+instance FromReqURI BlogLang where
+  fromReqURI sub =
+    case map toLower sub of
+      "de" -> Just DE
+      "en" -> Just EN
+      _    -> Nothing
+
+$(deriveSafeCopy 0 'base ''BlogLang)
+
+data Comment = Comment {
+    cdate   :: UTCTime,
+    cauthor :: Text,
+    ctext   :: Text
+} deriving (Eq, Ord, Show, Data, Typeable)
+
+$(deriveSafeCopy 0 'base ''Comment)
+
+data Entry_v0 = Entry_v0 {
+    entryId_v0  :: EntryId,
+    lang_v0     :: BlogLang,
+    author_v0   :: Text,
+    title_v0    :: Text,
+    btext_v0    :: Text,
+    mtext_v0    :: Text,
+    edate_v0    :: UTCTime,
+    tags     :: [Text],
+    comments :: [Comment]
+} deriving (Eq, Ord, Show, Data, Typeable)
+$(deriveSafeCopy 0 'base ''Entry_v0)
+
+data Entry = Entry {
+    entryId  :: EntryId,
+    lang     :: BlogLang,
+    author   :: Text,
+    title    :: Text,
+    btext    :: Text,
+    mtext    :: Text,
+    edate    :: UTCTime
+} deriving (Eq, Ord, Show, Data, Typeable)
+
+$(deriveSafeCopy 2 'extension ''Entry)
+
+instance Migrate Entry where
+  type MigrateFrom Entry = Entry_v0
+  migrate (Entry_v0 ei l a t b m ed _ _) =
+    Entry ei l a t b m ed
+  
+-- ixSet requires different datatypes for field indexes, so let's define some
+newtype Author_v0 = Author_v0 Text deriving (Eq, Ord, Data, Typeable, SafeCopy)
+newtype Author = Author Text   deriving (Eq, Ord, Data, Typeable)
+$(deriveSafeCopy 2 'extension ''Author)
+instance Migrate Author where
+  type MigrateFrom Author = Author_v0
+  migrate (Author_v0 x) = Author x
+
+newtype Title_v0  = Title_v0 Text    deriving (Eq, Ord, Data, Typeable, SafeCopy)
+newtype Title  = Title Text    deriving (Eq, Ord, Data, Typeable)
+$(deriveSafeCopy 2 'extension ''Title)
+instance Migrate Title where
+  type MigrateFrom Title = Title_v0
+  migrate (Title_v0 x) = Title x
+
+newtype BText_v0  = BText_v0 Text    deriving (Eq, Ord, Data, Typeable, SafeCopy)
+newtype BText  = BText Text    deriving (Eq, Ord, Data, Typeable) -- standard text
+$(deriveSafeCopy 2 'extension ''BText)
+instance Migrate BText where
+  type MigrateFrom BText = BText_v0
+  migrate (BText_v0 x) = BText x
+
+newtype MText_v0  = MText_v0 Text    deriving (Eq, Ord, Data, Typeable, SafeCopy)
+newtype MText  = MText Text    deriving (Eq, Ord, Data, Typeable) -- "read more" text
+$(deriveSafeCopy 2 'extension ''MText)
+instance Migrate MText where
+  type MigrateFrom MText = MText_v0
+  migrate (MText_v0 x) = MText x
+
+newtype Tag_v0    = Tag_v0 Text      deriving (Eq, Ord, Data, Typeable, SafeCopy)
+newtype Tag    = Tag Text      deriving (Eq, Ord, Data, Typeable)
+$(deriveSafeCopy 2 'extension ''Tag)
+instance Migrate Tag where
+  type MigrateFrom Tag = Tag_v0
+  migrate (Tag_v0 x) = Tag x
+
+newtype EDate_v0  = EDate_v0 UTCTime deriving (Eq, Ord, Data, Typeable, SafeCopy)
+newtype EDate  = EDate UTCTime deriving (Eq, Ord, Data, Typeable)
+$(deriveSafeCopy 2 'extension ''EDate)
+instance Migrate EDate where
+  type MigrateFrom EDate = EDate_v0
+  migrate (EDate_v0 x) = EDate x
+
+newtype SDate_v0  = SDate_v0 UTCTime   deriving (Eq, Ord, Data, Typeable, SafeCopy)
+newtype SDate  = SDate UTCTime   deriving (Eq, Ord, Data, Typeable)
+$(deriveSafeCopy 2 'extension ''SDate)
+instance Migrate SDate where
+  type MigrateFrom SDate = SDate_v0
+  migrate (SDate_v0 x) = SDate x
+
+newtype Username_v0 = Username_v0 Text deriving (Eq, Ord, Data, Typeable, SafeCopy)
+newtype Username = Username Text deriving (Eq, Ord, Data, Typeable)
+$(deriveSafeCopy 2 'extension ''Username)
+instance Migrate Username where
+  type MigrateFrom Username = Username_v0
+  migrate (Username_v0 x) = Username x
+  
+newtype SessionID_v0 = SessionID_v0 Text deriving (Eq, Ord, Data, Typeable, SafeCopy)
+newtype SessionID = SessionID Text deriving (Eq, Ord, Data, Typeable)
+$(deriveSafeCopy 2 'extension ''SessionID)
+instance Migrate SessionID where
+  type MigrateFrom SessionID = SessionID_v0
+  migrate (SessionID_v0 x) = SessionID x
+
+instance Indexable Entry where
+    empty = ixSet [ ixFun $ \e -> [ entryId e]
+                  , ixFun $ (:[]) . lang
+                  , ixFun $ \e -> [ Author $ author e ]
+                  , ixFun $ \e -> [ Title $ title e]
+                  , ixFun $ \e -> [ BText $ btext e]
+                  , ixFun $ \e -> [ MText $ mtext e]
+                  , ixFun $ \e -> [ EDate $ edate e]
+                  ]
+
+data User = User {
+    username :: Text,
+    password :: ByteString
+} deriving (Eq, Ord, Data, Typeable)
+
+$(deriveSafeCopy 0 'base ''User)
+
+data Session = Session {
+    sessionID :: Text,
+    user      :: User,
+    sdate     :: UTCTime
+} deriving (Eq, Ord, Data, Typeable)
+
+$(deriveSafeCopy 0 'base ''Session)
+
+instance Indexable User where
+    empty = ixSet [ ixFun $ \u -> [Username $ username u]
+                  , ixFun $ (:[]) . password
+                  ]
+
+instance Indexable Session where
+    empty = ixSet [ ixFun $ \s -> [SessionID $ sessionID s]
+                  , ixFun $ (:[]) . user
+                  , ixFun $ \s -> [SDate $ sdate s]
+                  ]
+
+data Blog = Blog {
+    blogSessions :: IxSet Session,
+    blogUsers    :: IxSet User,
+    blogEntries  :: IxSet Entry
+} deriving (Data, Typeable)
+
+latestEntries :: BlogLang -> Query Blog [Entry]
+latestEntries lang =
+    do b@Blog{..} <- ask
+       return $ IxSet.toDescList (Proxy :: Proxy EDate) $ blogEntries @= lang
+
+$(deriveSafeCopy 0 'base ''Blog)
+                         
+$(makeAcidic ''Blog ['latestEntries])
+
+initialBlogState :: Blog
+initialBlogState =
+    Blog { blogSessions = empty
+         , blogUsers = empty
+         , blogEntries = empty }
+
+main :: IO ()
+main = do
+  putStrLn "Opening state"
+  acid <- openLocalStateFrom "/var/tazblog/BlogState" initialBlogState
+  entries <- query acid (LatestEntries EN)
+  print $ length entries
+  print $ head entries
+  putStrLn "Creating checkpoint"
+  createCheckpoint acid
+  putStrLn "Closing state"
+  closeAcidState acid