@@ -10,6 +10,7 @@ import (
1010 "time"
1111
1212 "github.com/go-kit/log"
13+ "github.com/hashicorp/golang-lru/v2/expirable"
1314 "github.com/lib/pq"
1415 "go.uber.org/atomic"
1516
@@ -200,8 +201,13 @@ type foreignKey struct {
200201}
201202
202203type SchemaDetailsArguments struct {
203- DB * sql.DB
204- EntryHandler loki.EntryHandler
204+ DB * sql.DB
205+ CollectInterval time.Duration
206+ EntryHandler loki.EntryHandler
207+
208+ CacheEnabled bool
209+ CacheSize int
210+ CacheTTL time.Duration
205211
206212 Logger log.Logger
207213}
@@ -211,6 +217,11 @@ type SchemaDetails struct {
211217 collectInterval time.Duration
212218 entryHandler loki.EntryHandler
213219
220+ // Cache of table definitions. Entries are removed after a configurable TTL.
221+ // Key is a string of the form "database.schema.table".
222+ // (unlike MySQL) no create/update timestamp available for detecting immediately when a table schema is changed; relying on TTL only
223+ cache * expirable.LRU [string , * tableInfo ]
224+
214225 logger log.Logger
215226 running * atomic.Bool
216227 ctx context.Context
@@ -220,12 +231,16 @@ type SchemaDetails struct {
220231func NewSchemaDetails (args SchemaDetailsArguments ) (* SchemaDetails , error ) {
221232 c := & SchemaDetails {
222233 dbConnection : args .DB ,
223- collectInterval : 10 * time . Minute , // TODO: make it configurable again once caching is implemented
234+ collectInterval : args . CollectInterval ,
224235 entryHandler : args .EntryHandler ,
225236 logger : log .With (args .Logger , "collector" , SchemaDetailsCollector ),
226237 running : & atomic.Bool {},
227238 }
228239
240+ if args .CacheEnabled {
241+ c .cache = expirable .NewLRU [string , * tableInfo ](args .CacheSize , nil , args .CacheTTL )
242+ }
243+
229244 return c , nil
230245}
231246
@@ -354,10 +369,25 @@ func (c *SchemaDetails) extractNames(ctx context.Context) error {
354369 }
355370
356371 for _ , table := range tables {
357- table , err = c .fetchTableDefinitions (ctx , table )
358- if err != nil {
359- level .Error (c .logger ).Log ("msg" , "failed to get table definitions" , "datname" , dbName , "schema" , table .schema , "err" , err )
360- continue
372+ cacheKey := fmt .Sprintf ("%s.%s.%s" , table .database , table .schema , table .tableName )
373+
374+ cacheHit := false
375+ if c .cache != nil {
376+ if cached , ok := c .cache .Get (cacheKey ); ok {
377+ table = cached
378+ cacheHit = true
379+ }
380+ }
381+
382+ if ! cacheHit {
383+ table , err = c .fetchTableDefinitions (ctx , table )
384+ if err != nil {
385+ level .Error (c .logger ).Log ("msg" , "failed to get table definitions" , "datname" , dbName , "schema" , table .schema , "err" , err )
386+ continue
387+ }
388+ if c .cache != nil {
389+ c .cache .Add (cacheKey , table )
390+ }
361391 }
362392
363393 c .entryHandler .Chan () <- database_observability .BuildLokiEntry (
0 commit comments