The code of this example is identical to any one for persistence in files that you can find here except the procedure setAmazonSimpleDB that set the default persistence in Amazon simpleDB.
http://mflowdemo.herokuapp.com/noscript/database
As ever, MFlow permits to address all the pages in the flow with a single bookmark. You just enter the appropriate path and parameters. For example this URL:
http://mflowdemo.herokuapp.com/noscript/database/newtext
Present the entry form, that is the second page in the demo. And this URL:
http://mflowdemo.herokuapp.com/noscript/database/newtext?p0= text to enter
Fill the text box of the entry form, insert the text in the database and return to the first page again, since the demo has a loop.
Default persistence means that each register is stored in a small blob somewhere, either in a file -by default- or in another storage. For small blobs identified by keys, Amazon simpleDB is better than S3.
The default persistence is defined in the setDefaultPersist and setPersist. While the former -as its name says- set the default for any kind of data, this latter is per-datatype. It is a method defined in the Serializable instance in the DefaultPersistence module. The blob of each register is read and written by TCache and converted to a haskell register under a DBRef reference, in the STM monad. DBRefs follows the semantics of the TVar references.
TCache besides caching the registers, it permits querying and indexing them by a relational-like syntax using haskell register field names (see the query defined below in allTexts). The indexes used for querying are also registers that are stored and retrieved using the default persist mechanism. That means that the registers that are not used are discarded from the cache, and the modified indexes are stored in the persistent storage automatically, in this case, in SimpleDB
Caching and querying local indexes improves response time and reduces the number of accesses to the paid cloud infrastructure, so it reduces costs.
I´m not fully satisfied with the default persistence set in setAmazonSimpleDB I need to optimize the storage and the access using more features of AWS simpleDB and mix it with Amazon S3 for largue blogs, but this is a proof of concept.
This is the code:
{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}
module Database where
import MFlow.Wai.Blaze.Html.All hiding (select)
import Data.Typeable
import Data.TCache.IdexQuery
import Data.TCache.DefaultPersistence
import Data.TCache.Memoization
import Data.Monoid
import Menu
import Data.String
import Aws
import Aws.SimpleDb hiding (select)
import qualified Data.Text as T
import Data.Text.Encoding
import Data.ByteString.Lazy(toChunks,fromChunks)
import Network
import Debug.Trace
(!>)= flip trace
data MyData= MyData{idnumber ∷ Int, textdata ∷ T.Text} deriving (Typeable, Read, Show) -- that is enough for blob persistence
instance Indexable MyData where
key= show . idnumber -- to notify what is the key of the register
defPath = const ""
data Options= NewText | Exit deriving (Show, Typeable)
main= do
setAmazonSimpleDB
syncWrite $ Asyncronous 120 defaultCheck 1000
index idnumber
runNavigation "" $ transientNav $ do
all ← allTexts
r ← ask $ listtexts all
case r of
NewText → do
text ← ask $ p << "insert the text"
++> getMultilineText "" <++ br
<** submitButton "enter"
-- store the name in the cache
-- (later will be written to disk automatically)
liftIO . atomically . newDBRef $ MyData (length all) text
Exit → return ()
where
menu= wlink NewText << p << "enter a new text" <|>
wlink Exit << p << "exit to the main menu"
listtexts all = do
h3 << "list of all texts"
++> mconcat[p << t | t ← all]
++> menu
<++ b << "or the back button for a new database action"
allTexts= liftIO . atomically . select textdata $ idnumber .>=. (0 ∷ Int)
sdbCfg = defServiceConfig
domain = fromString "mflowdemotest"
setAmazonSimpleDB = withSocketsDo $ do
cfg ← baseConfiguration
-- simpleAws cfg sdbCfg $ deleteDomain domain
simpleAws cfg sdbCfg $ createDomain domain -- delete once created
setDefaultPersist $ Persist{
readByKey= λkey → withSocketsDo $ do
r ← simpleAws cfg sdbCfg $ getAttributes (T.pack key) domain
case r of
GetAttributesResponse [ForAttribute _ text] → return $ Just
$ fromChunks [encodeUtf8 text]
_ → return Nothing,
write= λkey str → withSocketsDo $ do
simpleAws cfg sdbCfg
$ putAttributes (T.pack key)
[ForAttribute tdata
(SetAttribute
(T.concat $ map decodeUtf8 $ toChunks str)
True)] domain
return (),
delete= λkey → withSocketsDo $ do
simpleAws cfg sdbCfg
$ deleteAttributes (T.pack key)
[ForAttribute tdata DeleteAttribute] domain
return ()
}
tdata= fromString "textdata"
No comments:
Post a Comment