fix: Performance and high memory usage of relation hint calculation
* Calculation of hint message when requested relation is not present in schema cache requires creation of a FuzzySet (to use fuzzy search to find candidate tables). For schemas with many tables it is costly. This patch introduces dbTablesFuzzyIndex in SchemaCache to memoize the FuzzySet creation. * Additionally, because of FuzzySet large memory requirements, this patch introduces a limit of 500 relations per schema, above which FuzzySet is not created and hint calculation disabled.
This commit is contained in:
committed by
Steve Chavez
parent
9ec5b030ce
commit
e592d568c6
@@ -20,6 +20,7 @@ These queries are executed once at startup or when PostgREST is reloaded.
|
||||
|
||||
module PostgREST.SchemaCache
|
||||
( SchemaCache(..)
|
||||
, TablesFuzzyIndex
|
||||
, querySchemaCache
|
||||
, showSummary
|
||||
, decodeFuncs
|
||||
@@ -66,21 +67,28 @@ import PostgREST.SchemaCache.Table (Column (..), ColumnMap,
|
||||
|
||||
import qualified PostgREST.MediaType as MediaType
|
||||
|
||||
import Control.Arrow ((&&&))
|
||||
import Protolude
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
import Control.Arrow ((&&&))
|
||||
import qualified Data.FuzzySet as Fuzzy
|
||||
import Protolude
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
|
||||
type TablesFuzzyIndex = HM.HashMap Schema Fuzzy.FuzzySet
|
||||
|
||||
data SchemaCache = SchemaCache
|
||||
{ dbTables :: TablesMap
|
||||
, dbRelationships :: RelationshipsMap
|
||||
, dbRoutines :: RoutineMap
|
||||
, dbRepresentations :: RepresentationsMap
|
||||
, dbMediaHandlers :: MediaHandlerMap
|
||||
, dbTimezones :: TimezoneNames
|
||||
}
|
||||
{ dbTables :: TablesMap
|
||||
, dbRelationships :: RelationshipsMap
|
||||
, dbRoutines :: RoutineMap
|
||||
, dbRepresentations :: RepresentationsMap
|
||||
, dbMediaHandlers :: MediaHandlerMap
|
||||
, dbTimezones :: TimezoneNames
|
||||
-- Memoized fuzzy index of table names per schema to support approximate matching
|
||||
-- Since index construction can be expensive, we build it once and store in the SchemaCache
|
||||
-- Haskell lazy evaluation ensures it's only built on first use and memoized afterwards
|
||||
, dbTablesFuzzyIndex :: TablesFuzzyIndex
|
||||
} deriving (Show)
|
||||
|
||||
instance JSON.ToJSON SchemaCache where
|
||||
toJSON (SchemaCache tabs rels routs reps hdlers tzs) = JSON.object [
|
||||
toJSON (SchemaCache tabs rels routs reps hdlers tzs _) = JSON.object [
|
||||
"dbTables" .= JSON.toJSON tabs
|
||||
, "dbRelationships" .= JSON.toJSON rels
|
||||
, "dbRoutines" .= JSON.toJSON routs
|
||||
@@ -90,7 +98,7 @@ instance JSON.ToJSON SchemaCache where
|
||||
]
|
||||
|
||||
showSummary :: SchemaCache -> Text
|
||||
showSummary (SchemaCache tbls rels routs reps mediaHdlrs tzs) =
|
||||
showSummary (SchemaCache tbls rels routs reps mediaHdlrs tzs _) =
|
||||
T.intercalate ", "
|
||||
[ show (HM.size tbls) <> " Relations"
|
||||
, show (HM.size rels) <> " Relationships"
|
||||
@@ -138,6 +146,8 @@ data KeyDep
|
||||
-- | A SQL query that can be executed independently
|
||||
type SqlQuery = ByteString
|
||||
|
||||
maxDbTablesForFuzzySearch :: Int
|
||||
maxDbTablesForFuzzySearch = 500
|
||||
|
||||
querySchemaCache :: AppConfig -> SQL.Transaction SchemaCache
|
||||
querySchemaCache conf@AppConfig{..} = do
|
||||
@@ -166,6 +176,11 @@ querySchemaCache conf@AppConfig{..} = do
|
||||
, dbRepresentations = reps
|
||||
, dbMediaHandlers = HM.union mHdlers initialMediaHandlers -- the custom handlers will override the initial ones
|
||||
, dbTimezones = tzones
|
||||
|
||||
, dbTablesFuzzyIndex =
|
||||
-- Only build fuzzy index for schemas with a reasonable number of tables
|
||||
-- Fuzzy.FuzzySet is memory heavy we just don't use it for large schemas
|
||||
Fuzzy.fromList <$> HM.filter ((< maxDbTablesForFuzzySearch) . length) (HM.fromListWith (<>) ((qiSchema &&& pure . qiName) <$> HM.keys tabsWViewsPks))
|
||||
}
|
||||
where
|
||||
schemas = toList configDbSchemas
|
||||
@@ -203,6 +218,7 @@ removeInternal schemas dbStruct =
|
||||
, dbRepresentations = dbRepresentations dbStruct -- no need to filter, not directly exposed through the API
|
||||
, dbMediaHandlers = dbMediaHandlers dbStruct
|
||||
, dbTimezones = dbTimezones dbStruct
|
||||
, dbTablesFuzzyIndex = dbTablesFuzzyIndex dbStruct
|
||||
}
|
||||
where
|
||||
hasInternalJunction ComputedRelationship{} = False
|
||||
|
||||
Reference in New Issue
Block a user