-
Notifications
You must be signed in to change notification settings - Fork 696
Add CacheLatestTtlSecs to allow expiration of latest schemas #1106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3f5fe3b
33a2792
3e17ad4
1eb355b
54f242c
4a459a8
2705176
9987830
4574f3a
0fafc44
f4c8def
f4a2497
8943acf
21f300e
a903481
4b7e618
beef773
8c503c0
2770efe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,8 +20,10 @@ import ( | |
| "encoding/json" | ||
| "fmt" | ||
| "net/url" | ||
| "runtime" | ||
| "strings" | ||
| "sync" | ||
| "time" | ||
|
|
||
| "github.com/confluentinc/confluent-kafka-go/v2/schemaregistry/cache" | ||
| ) | ||
|
|
@@ -189,6 +191,9 @@ type client struct { | |
| schemaToVersionCacheLock sync.RWMutex | ||
| versionToSchemaCache cache.Cache | ||
| versionToSchemaCacheLock sync.RWMutex | ||
| latestToSchemaCache cache.Cache | ||
| latestToSchemaCacheLock sync.RWMutex | ||
| evictor *evictor | ||
| } | ||
|
|
||
| var _ Client = new(client) | ||
|
|
@@ -243,6 +248,7 @@ func NewClient(conf *Config) (Client, error) { | |
| var idToSchemaCache cache.Cache | ||
| var schemaToVersionCache cache.Cache | ||
| var versionToSchemaCache cache.Cache | ||
| var latestToSchemaCache cache.Cache | ||
| if conf.CacheCapacity != 0 { | ||
| schemaToIDCache, err = cache.NewLRUCache(conf.CacheCapacity) | ||
| if err != nil { | ||
|
|
@@ -260,18 +266,28 @@ func NewClient(conf *Config) (Client, error) { | |
| if err != nil { | ||
| return nil, err | ||
| } | ||
| latestToSchemaCache, err = cache.NewLRUCache(conf.CacheCapacity) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| } else { | ||
| schemaToIDCache = cache.NewMapCache() | ||
| idToSchemaCache = cache.NewMapCache() | ||
| schemaToVersionCache = cache.NewMapCache() | ||
| versionToSchemaCache = cache.NewMapCache() | ||
| latestToSchemaCache = cache.NewMapCache() | ||
| } | ||
| handle := &client{ | ||
| restService: restService, | ||
| schemaToIDCache: schemaToIDCache, | ||
| idToSchemaCache: idToSchemaCache, | ||
| schemaToVersionCache: schemaToVersionCache, | ||
| versionToSchemaCache: versionToSchemaCache, | ||
| latestToSchemaCache: latestToSchemaCache, | ||
| } | ||
| if conf.CacheLatestTTLSecs > 0 { | ||
| runEvictor(handle, time.Duration(conf.CacheLatestTTLSecs)*time.Second) | ||
| runtime.SetFinalizer(handle, stopEvictor) | ||
| } | ||
| return handle, nil | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Coulse use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, let me make that change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @emasab , I added the finalizer |
||
| } | ||
|
|
@@ -393,7 +409,26 @@ func (c *client) GetID(subject string, schema SchemaInfo, normalize bool) (id in | |
| // GetLatestSchemaMetadata fetches latest version registered with the provided subject | ||
| // Returns SchemaMetadata object | ||
| func (c *client) GetLatestSchemaMetadata(subject string) (result SchemaMetadata, err error) { | ||
| return c.GetSchemaMetadata(subject, -1) | ||
| c.latestToSchemaCacheLock.RLock() | ||
| metadataValue, ok := c.latestToSchemaCache.Get(subject) | ||
| c.latestToSchemaCacheLock.RUnlock() | ||
| if ok { | ||
| return *metadataValue.(*SchemaMetadata), nil | ||
| } | ||
|
|
||
| c.latestToSchemaCacheLock.Lock() | ||
| // another goroutine could have already put it in cache | ||
| metadataValue, ok = c.latestToSchemaCache.Get(subject) | ||
| if !ok { | ||
| err = c.restService.handleRequest(newRequest("GET", versions, nil, url.PathEscape(subject), "latest"), &result) | ||
| if err == nil { | ||
| c.latestToSchemaCache.Put(subject, &result) | ||
| } | ||
| } else { | ||
| result = *metadataValue.(*SchemaMetadata) | ||
| } | ||
| c.latestToSchemaCacheLock.Unlock() | ||
| return result, err | ||
| } | ||
|
|
||
| // GetSchemaMetadata fetches the requested subject schema identified by version | ||
|
|
@@ -687,3 +722,34 @@ func (c *client) UpdateDefaultCompatibility(update Compatibility) (compatibility | |
|
|
||
| return result.CompatibilityUpdate, err | ||
| } | ||
|
|
||
| type evictor struct { | ||
| Interval time.Duration | ||
| stop chan bool | ||
| } | ||
|
|
||
| func (e *evictor) Run(c cache.Cache) { | ||
| ticker := time.NewTicker(e.Interval) | ||
| for { | ||
| select { | ||
| case <-ticker.C: | ||
| c.Clear() | ||
| case <-e.stop: | ||
| ticker.Stop() | ||
| return | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func stopEvictor(c *client) { | ||
| c.evictor.stop <- true | ||
| } | ||
|
|
||
| func runEvictor(c *client, ci time.Duration) { | ||
| e := &evictor{ | ||
| Interval: ci, | ||
| stop: make(chan bool), | ||
| } | ||
| c.evictor = e | ||
| go e.Run(c.latestToSchemaCache) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a more efficient way to clear than iterating through all the elements?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is idomatic go before go 1.21, where they added a
clear()method. In any case, these maps are not expected to get that large as they store one entry per subject