Skip to content

Commit c254596

Browse files
committed
improve performance of getText
1 parent 4e5af36 commit c254596

File tree

4 files changed

+85
-22
lines changed

4 files changed

+85
-22
lines changed

service/postgres/init.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,9 @@ CREATE INDEX idx_text_paragraph_era
398398
CREATE INDEX idx_text_paragraph_era_source_book
399399
ON public.text_paragraph (era, source, book);
400400

401+
CREATE INDEX idx_text_paragraph_era_source_book_id
402+
ON text_paragraph(era, source, book, id);
403+
401404
-- For replay
402405
CREATE INDEX idx_replay_user_id
403406
ON public.replay (user_id);

service/src/Application.hs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ where
2323

2424
-- Import all relevant handler modules here.
2525
import Control.Monad.Logger (liftLoc, runLoggingT)
26+
import qualified Data.Map.Strict as Map
2627
import Database.Persist.Postgresql
2728
( createPostgresqlPool,
2829
pgConnStr,
2930
pgPoolSize,
3031
runSqlPool,
3132
)
33+
import Database.Persist.Sql (Single (..), rawSql)
3234
import Handler.Common
3335
import Handler.CustomText
3436
import Handler.Home
@@ -82,6 +84,7 @@ makeFoundation appSettings = do
8284
appLobbies <- newTVarIO mempty
8385
gen <- newStdGen
8486
appRng <- newIORef gen
87+
appMinMaxByEra <- newIORef Map.empty
8588

8689
let mkFoundation appConnPool = App {..}
8790
tempFoundation = mkFoundation $ error "connPool forced in tempFoundation"
@@ -97,6 +100,21 @@ makeFoundation appSettings = do
97100
-- Perform database migration using our application's logging settings.
98101
runLoggingT (runSqlPool (runMigration migrateAll) pool) logFunc
99102

103+
-- Load MIN/MAX IDs per era into memory
104+
minMaxRows <-
105+
flip runSqlPool pool $
106+
rawSql
107+
"SELECT era, MIN(id), MAX(id) \
108+
\FROM text_paragraph \
109+
\GROUP BY era"
110+
[]
111+
let minMaxMap =
112+
Map.fromList
113+
[ (era, (minId, maxId))
114+
| (Single era, Single (Just minId), Single (Just maxId)) <- minMaxRows
115+
]
116+
writeIORef appMinMaxByEra minMaxMap
117+
100118
-- Return the foundation
101119
return $ mkFoundation pool
102120

service/src/Foundation.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Control.Monad.Logger (LogSource)
1717
import qualified Data.ByteString.Lazy as LBS
1818
import qualified Data.CaseInsensitive as CI
1919
import Data.Kind (Type)
20+
import qualified Data.Map.Strict as Map
2021
import qualified Data.Text.Encoding as TE
2122
import Database.Persist.Sql (ConnectionPool, runSqlPool)
2223
import Import.NoFoundation
@@ -37,7 +38,8 @@ data App = App
3738
appHttpManager :: Manager,
3839
appLogger :: Logger,
3940
appLobbies :: TVar (Map Text LobbyState),
40-
appRng :: IORef StdGen
41+
appRng :: IORef StdGen,
42+
appMinMaxByEra :: IORef (Map.Map Text (Int64, Int64))
4143
}
4244

4345
type ServerMessage = LBS.ByteString

service/src/Handler/Text.hs

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,80 @@
66
module Handler.Text where
77

88
import Data.ByteString (hGet)
9+
import qualified Data.Map.Strict as Map
910
import qualified Data.Text as T
11+
import Database.Persist.Sql (Single (Single), rawSql, toSqlKey)
1012
import Import
1113
import System.Directory (doesFileExist)
1214
import System.Random (randomRIO)
1315

14-
getRandomParagraph :: Text -> Text -> Int -> Handler (Entity TextParagraph)
16+
getRandomParagraph ::
17+
Text -> Text -> Int -> Handler (Entity TextParagraph)
1518
getRandomParagraph era source book = do
16-
let filters =
19+
res <-
20+
runDB $
21+
rawSql
22+
"SELECT MIN(id), MAX(id) \
23+
\FROM text_paragraph \
24+
\WHERE era = ? \
25+
\ AND source = ? \
26+
\ AND book = ?"
27+
[ toPersistValue era,
28+
toPersistValue source,
29+
toPersistValue book
30+
]
31+
pair <- case res of
32+
[(Single mMinId, Single mMaxId)] ->
33+
let minId = fromMaybe (error "no rows") mMinId
34+
maxId = fromMaybe minId mMaxId
35+
in return (minId, maxId)
36+
_ -> notFound
37+
let (minId, maxId) = pair
38+
rnd <- liftIO $ randomRIO (minId, maxId)
39+
let baseFilters =
1740
[ TextParagraphEra ==. era,
1841
TextParagraphSource ==. source,
1942
TextParagraphBook ==. book
2043
]
21-
total <- runDB $ count filters
22-
when (total == 0) notFound
23-
24-
idx <- liftIO $ randomRIO (0, total - 1)
25-
26-
results <- runDB $ selectList filters [OffsetBy idx, LimitTo 1]
27-
case results of
28-
[ent] -> return ent
29-
_ -> error "No paragraph found for this era."
44+
mEnt <-
45+
runDB $
46+
selectFirst
47+
(baseFilters ++ [TextParagraphId >=. toSqlKey rnd])
48+
[Asc TextParagraphId]
49+
case mEnt of
50+
Just ent -> return ent
51+
Nothing -> do
52+
mEnt2 <-
53+
runDB $
54+
selectFirst
55+
baseFilters
56+
[Asc TextParagraphId]
57+
maybe notFound return mEnt2
3058

3159
getRandomNewText :: Text -> Handler (Entity TextParagraph)
3260
getRandomNewText era = do
33-
let filters = [TextParagraphEra ==. era]
34-
total <- runDB $ count filters
35-
when (total == 0) notFound
36-
37-
idx <- liftIO $ randomRIO (0, total - 1)
38-
39-
results <- runDB $ selectList filters [OffsetBy idx, LimitTo 1]
40-
case results of
41-
[ent] -> return ent
42-
_ -> error "No paragraph found for this era."
61+
App {appMinMaxByEra = cacheRef} <- getYesod
62+
cache <- liftIO $ readIORef cacheRef
63+
case Map.lookup era cache of
64+
Nothing -> notFound
65+
Just (minId, maxId) -> do
66+
rnd <- liftIO $ randomRIO (minId, maxId)
67+
mEnt <-
68+
runDB $
69+
selectFirst
70+
[ TextParagraphEra ==. era,
71+
TextParagraphId >=. toSqlKey rnd
72+
]
73+
[Asc TextParagraphId]
74+
case mEnt of
75+
Just ent -> return ent
76+
Nothing -> do
77+
mEnt2 <-
78+
runDB $
79+
selectFirst
80+
[TextParagraphEra ==. era]
81+
[Asc TextParagraphId]
82+
maybe notFound return mEnt2
4383

4484
getCustomText :: UserId -> Text -> Handler ByteString
4585
getCustomText userId filename = do

0 commit comments

Comments
 (0)