Tuesday, June 18, 2013

Caching Web pages with TCache. Translation of a java example that uses JCache

I just read this introduction to JCache because I was curious about the similarities with my package TCache. While JCache is an standard interface for distributed HashMaps, like memcached or Resin, TCache is an implementation of a transactional cache that is not yet distributed -but I plan it to be.

But the latter has a richer semantic, since It has not only search by key, but also support the STM semantic of updates and retrievals. It also has a query language using Haskell field names and various kinds of indexations (and it is more type safe).

Java is extraordinarily verbose, and the Java people enjoy adding even more verbosity to their frameworks and standards (except perhaps the engineers at Google). 

I translated the same example in the refered article to TCache and MFlow. I will use seconds instead of milliseconds

module Main where

import MFlow.Wai.Blaze.Html.All
import Data.TCache.Memoization
import System.Time
import Control.Concurrent  -- for the console example

seconds :: IO String
seconds= do
         TOD t _ <- liftIO getClockTime
         return $ show t

-- generates the page in Text.Blaze.Html format
page :: IO Html
page= do
   t <-  seconds 
   return $ p << ("Hello World " ++ t)

-- caches the Html code
cachedPage:: IO Html
cachedPage = cachedByKey "hello message" 10 page

helloServlet=  do
      msg <- liftIO $  cachedPage
      msg ++>  noWidget

That's all the code of the Java example. cachedByKey stores the value of the computation in the cache for ten second with the key "hello message".  When the value is requested after ten seconds, the computation is re-executed again.

The operator << is a convenience operator defined in MFlow to avoid the overloaded strings extension, but what is cached in cachedPage is pure blaze-html markup. So cachedByKey can be used in any other Haskell web framework to cache Html pages.

helloServlet can also be written using wcached or wfreeze, which is specific for caching MFlow widgets:

helloServlet'= wfreeze  "hello message Widget" 10 $ messageWidget 

messageWidget ::  View Html IO ()
messageWidget= liftIO page >>= \msg -> msg ++> noWidget 

noWidget is a dumb widget that has no rendering.

To execute both servlet in the port 80:

main= do

   addMessageFlows[("noscript",wstateless helloServlet)
                   ,("alternative", wstateless helloServlet')]

   wait $ run 80 waiMessageFlow

The procedure associated to "noscript" is invoked when no path is in the URL.

A similar console program, which polls the cache every second:

main= do
      msg <-  cachedByKey "hello message text" 10 messageText
      print msg
      threadDelay 1000000

   t <- seconds 
   return $ p <<("Hello World " ++ t)

Post a Comment