Skip to content

Commit 3f7f464

Browse files
authored
database_observability: add caching to Postgres schema details collector (#4565)
1 parent 597291e commit 3f7f464

File tree

5 files changed

+385
-39
lines changed

5 files changed

+385
-39
lines changed

docs/sources/reference/components/database_observability/database_observability.postgres.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,13 @@ You can use the following blocks with `database_observability.postgres`:
7373

7474
### `schema_details`
7575

76-
This collector has no config options.
76+
| Name | Type | Description | Default | Required |
77+
|--------------------|------------|-----------------------------------------------------------------------|---------|----------|
78+
| `collect_interval` | `duration` | How frequently to collect information from database. | `"1m"` | no |
79+
| `cache_enabled` | `boolean` | Whether to enable caching of table definitions. | `true` | no |
80+
| `cache_size` | `integer` | Cache size. | `256` | no |
81+
| `cache_ttl` | `duration` | Cache TTL. | `"10m"` | no |
82+
7783

7884
### `explain_plans`
7985

internal/component/database_observability/postgres/collector/schema_details.go

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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

202203
type 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 {
220231
func 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

Comments
 (0)