-
Notifications
You must be signed in to change notification settings - Fork 6k
infoschema: fix issue of information schema cache miss cause by schema version gap #53445
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
f528f02
56d4904
54d4508
6eda953
f878627
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 |
---|---|---|
|
@@ -32,6 +32,9 @@ type InfoCache struct { | |
// cache is sorted by both SchemaVersion and timestamp in descending order, assume they have same order | ||
cache []schemaAndTimestamp | ||
|
||
// emptySchemaVersions stores schema version which has no schema_diff. | ||
emptySchemaVersions map[int64]struct{} | ||
|
||
r autoid.Requirement | ||
Data *Data | ||
} | ||
|
@@ -45,9 +48,10 @@ type schemaAndTimestamp struct { | |
func NewCache(r autoid.Requirement, capacity int) *InfoCache { | ||
infoData := NewData() | ||
return &InfoCache{ | ||
cache: make([]schemaAndTimestamp, 0, capacity), | ||
r: r, | ||
Data: infoData, | ||
cache: make([]schemaAndTimestamp, 0, capacity), | ||
emptySchemaVersions: make(map[int64]struct{}), | ||
r: r, | ||
Data: infoData, | ||
} | ||
} | ||
|
||
|
@@ -100,6 +104,11 @@ func (h *InfoCache) Len() int { | |
return len(h.cache) | ||
} | ||
|
||
// GetEmptySchemaVersions returns emptySchemaVersions, exports for testing. | ||
func (h *InfoCache) GetEmptySchemaVersions() map[int64]struct{} { | ||
return h.emptySchemaVersions | ||
} | ||
|
||
func (h *InfoCache) getSchemaByTimestampNoLock(ts uint64) (InfoSchema, bool) { | ||
logutil.BgLogger().Debug("SCHEMA CACHE get schema", zap.Uint64("timestamp", ts)) | ||
// search one by one instead of binary search, because the timestamp of a schema could be 0 | ||
|
@@ -117,11 +126,32 @@ func (h *InfoCache) getSchemaByTimestampNoLock(ts uint64) (InfoSchema, bool) { | |
// the first element is the latest schema, so we can return it directly. | ||
return is.infoschema, true | ||
} | ||
if h.cache[i-1].infoschema.SchemaMetaVersion() == is.infoschema.SchemaMetaVersion()+1 && uint64(h.cache[i-1].timestamp) > ts { | ||
// This first condition is to make sure the schema version is continuous. If last(cache[i-1]) schema-version is 10, | ||
// but current(cache[i]) schema-version is not 9, then current schema is not suitable for ts. | ||
// The second condition is to make sure the cache[i-1].timestamp > ts >= cache[i].timestamp, then the current schema is suitable for ts. | ||
return is.infoschema, true | ||
|
||
if uint64(h.cache[i-1].timestamp) > ts { | ||
// The first condition is to make sure the cache[i-1].timestamp > ts >= cache[i].timestamp, then the current schema is suitable for ts. | ||
lastVersion := h.cache[i-1].infoschema.SchemaMetaVersion() | ||
currentVersion := is.infoschema.SchemaMetaVersion() | ||
if lastVersion == currentVersion+1 { | ||
// This condition is to make sure the schema version is continuous. If last(cache[i-1]) schema-version is 10, | ||
// but current(cache[i]) schema-version is not 9, then current schema may not suitable for ts. | ||
return is.infoschema, true | ||
} | ||
if lastVersion > currentVersion { | ||
found := true | ||
for ver := currentVersion + 1; ver < lastVersion; ver++ { | ||
_, ok := h.emptySchemaVersions[ver] | ||
if !ok { | ||
found = false | ||
break | ||
} | ||
} | ||
if found { | ||
// This condition is to make sure the schema version is continuous. If last(cache[i-1]) schema-version is 10, and | ||
// current(cache[i]) schema-version is 8, then there is a gap exist, and if all the gap version can be found in cache.emptySchemaVersions | ||
// which means those gap versions don't have schema info, then current schema is also suitable for ts. | ||
return is.infoschema, true | ||
} | ||
} | ||
} | ||
// current schema is not suitable for ts, then break the loop to avoid the unnecessary search. | ||
break | ||
|
@@ -241,3 +271,25 @@ func (h *InfoCache) Insert(is InfoSchema, schemaTS uint64) bool { | |
|
||
return true | ||
} | ||
|
||
// InsertEmptySchemaVersion inserts empty schema version into a map. If exceeded the cache capacity, remove the oldest version. | ||
func (h *InfoCache) InsertEmptySchemaVersion(version int64) { | ||
h.mu.Lock() | ||
defer h.mu.Unlock() | ||
|
||
h.emptySchemaVersions[version] = struct{}{} | ||
if len(h.emptySchemaVersions) > cap(h.cache) { | ||
// remove oldest version. | ||
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. without statistics of how those version are accessed, remove oldest is no better than remove random version, and more complex 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. For stale-read query workload, it will always need recent schema version, so remove the oldest version is better. |
||
versions := make([]int64, 0, len(h.emptySchemaVersions)) | ||
for ver := range h.emptySchemaVersions { | ||
versions = append(versions, ver) | ||
} | ||
sort.Slice(versions, func(i, j int) bool { return versions[i] < versions[j] }) | ||
for _, ver := range versions { | ||
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. Is removing one version enough? 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. Because empty schema version rarely appears, I think it's okay to delete just one here. |
||
delete(h.emptySchemaVersions, ver) | ||
if len(h.emptySchemaVersions) <= cap(h.cache) { | ||
break | ||
} | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.