Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
23a9e24
resource_group: implement async loading for resource groups to improv…
disksing Sep 16, 2025
966d279
tests: wait for resource group reload after restart
bufferflies Jun 11, 2026
fae9a3e
resource_group: avoid overwriting default during async load
bufferflies Jun 11, 2026
f7cf2cd
tests: avoid direct proto dependency in RM integration
bufferflies Jun 11, 2026
a20e813
resource_group: fix lazy-load ordering and default-group review findings
bufferflies Jul 8, 2026
cbe73bc
resource_group: propagate non-not-found errors in AcquireTokenBuckets
bufferflies Jul 8, 2026
b23dd8f
Merge remote-tracking branch 'origin/master' into fix-10873-review-co…
bufferflies Jul 13, 2026
25ec642
resource_group: don't mark lazily-loaded group synced on state load f…
bufferflies Jul 13, 2026
306fd57
resource_group: distinguish reserved default placeholder from confirm…
bufferflies Jul 14, 2026
042d6a7
resource_group: keep placeholder state out of the persist loop and re…
bufferflies Jul 14, 2026
d2cddc9
resource_group: route default-group creation through the safe load path
bufferflies Jul 15, 2026
8f743d9
resource_group: don't let Modify confirm a state-unconfirmed group
bufferflies Jul 15, 2026
eb3e774
resource_group: add lazy-load coverage for legacy null-keyspace groups
bufferflies Jul 15, 2026
a32358a
resource_group: clear reserved marker when async merge installs confi…
bufferflies Jul 16, 2026
44e1d75
resource_group: fix unparam lint in async loading tests
bufferflies Jul 20, 2026
5fe46ba
resource_group: don't let a racing lazy load resurrect a deleted group
bufferflies Jul 20, 2026
229cd5d
resource_group: prevent a stale async loader from polluting a new term
bufferflies Jul 22, 2026
10b6c2a
resource_group: don't let the bulk merge clobber modified settings
bufferflies Jul 22, 2026
64e1313
resource_group: persist fresh-store default and retry lazy loads on d…
bufferflies Jul 22, 2026
57475ed
resource_group: guard token bucket state writes with the group lock
bufferflies Jul 22, 2026
78e9e69
address comment
bufferflies Jul 23, 2026
3766698
resource_group: use warn level for retryable bulk load failures
bufferflies Jul 23, 2026
7fe5201
resource_group: keep cross-term lazy loads out of the new term's state
bufferflies Jul 23, 2026
017899a
resource_group: publish metadata mutations against the current manager
bufferflies Jul 24, 2026
58186a0
resource_group: confirm a modified default instead of leaving it rese…
bufferflies Jul 24, 2026
2b7cbef
resource_group: preserve confirmed state when republishing a modified…
bufferflies Jul 24, 2026
308788c
resource_group: merge loaded groups in bounded batches
bufferflies Jul 24, 2026
2948880
resource_group: gate reserved-default backfill on the load epoch
bufferflies Jul 24, 2026
4a9a31d
resource_group: harden async loading error paths and observability
bufferflies Jul 26, 2026
458e69f
resource_group: close remaining async-load epoch/watcher races
bufferflies Jul 27, 2026
1d7726f
resource_group: stop racy default backfill and fix retry status code
bufferflies Jul 28, 2026
f8d99b2
resource_group: serialize default group synthesis against real writes
bufferflies Jul 30, 2026
ba8534d
resource_group: guard cross-term mutation publish and narrow default-…
bufferflies Aug 3, 2026
d16df5d
resource_group: document a known gap in cross-term Delete publish
bufferflies Aug 3, 2026
34eacab
Merge remote-tracking branch 'upstream/master' into pr-cp-10872-async…
bufferflies Aug 4, 2026
3042dde
resource_group: adapt KeyspaceIDValue literal to the oneof API
bufferflies Aug 4, 2026
3069a85
resource_group: link the cross-term Delete publish gap to tikv/pd#11105
bufferflies Aug 4, 2026
ee42b55
resource_group: dedupe stale initDefaultResourceGroup doc comment, re…
bufferflies Aug 5, 2026
49d5f10
resource_group: distinguish stale-term and real errors in initDefault…
bufferflies Aug 5, 2026
4efec59
resource_group: fix SetKeyspaceServiceLimit to survive a cross-term l…
bufferflies Aug 5, 2026
ab938b6
resource_group: fix two data races between config writes and a leader…
bufferflies Aug 5, 2026
d82f878
resource_group: fix watcher-mode default clobbering and SetKeyspaceSe…
bufferflies Aug 5, 2026
0ec8dab
resource_group: close two more cross-term races found in follow-up re…
bufferflies Aug 5, 2026
e6b896e
resource_group: route default-group synthesis through publishResource…
bufferflies Aug 5, 2026
45df73d
resource_group: skip syncBurstabilityWithServiceLimitLocked's group l…
bufferflies Aug 6, 2026
984e64d
resource_group: simplify locking helpers and dedupe error-wrapping
bufferflies Aug 6, 2026
b35672d
resource_group: retry and fall back to bulk value when loadServiceLim…
bufferflies Aug 6, 2026
422a3cf
resource_group: don't let loadServiceLimits' fallback clobber a concu…
bufferflies Aug 6, 2026
6cc24c7
resource_group: fail fast on Add/Modify/Delete's parked-before-storag…
bufferflies Aug 7, 2026
e26e741
mcs, resourcemanager: hold the lock across SetKeyspaceRUVersion's save
bufferflies Aug 11, 2026
619761d
mcs, resourcemanager: hold the lock across initControllerConfig's save
bufferflies Aug 12, 2026
716cbbe
mcs, resourcemanager: table-drive the three abort-on-newer-confirmed-…
bufferflies Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions errors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,11 @@ error = '''
keyspace not found with name: %s
'''

["PD:resourcemanager:ErrResourceGroupsLoading"]
error = '''
resource groups are still being loaded, please try again later
'''

["PD:scatter:ErrEmptyRegion"]
error = '''
empty region
Expand Down
11 changes: 11 additions & 0 deletions pkg/errs/errno.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ var (
ErrGRPCRateLimitExceeded = func(err error) error {
return status.Error(codes.ResourceExhausted, err.Error())
}
// ErrResourceGroupsLoadingGRPC converts the retryable
// ErrResourceGroupsLoading into codes.Unavailable, so generic
// client-side retry logic can act on it instead of seeing an opaque
// codes.Unknown. Other errors pass through unchanged.
ErrResourceGroupsLoadingGRPC = func(err error) error {
if ErrResourceGroupsLoading.Equal(err) {
return status.Error(codes.Unavailable, err.Error())
}
return err
}

// FailedPrecondition indicates operation was rejected because the
// system is not in a state required for the operation's execution.
Expand Down Expand Up @@ -538,6 +548,7 @@ var (
ErrResourceGroupNotExists = errors.Normalize("the %s resource group does not exist", errors.RFCCodeText("PD:resourcemanager:ErrGroupNotExists"))
ErrDeleteReservedGroup = errors.Normalize("cannot delete reserved group", errors.RFCCodeText("PD:resourcemanager:ErrDeleteReservedGroup"))
ErrInvalidGroup = errors.Normalize("invalid group settings, please check %s", errors.RFCCodeText("PD:resourcemanager:ErrInvalidGroup"))
ErrResourceGroupsLoading = errors.Normalize("resource groups are still being loaded, please try again later", errors.RFCCodeText("PD:resourcemanager:ErrResourceGroupsLoading"))
)

// Microservice errors
Expand Down
11 changes: 11 additions & 0 deletions pkg/mcs/resourcemanager/metadataapi/config_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,13 @@ func (*ConfigService) respondStoreReadError(c *gin.Context, err error) {
c.String(http.StatusNotFound, err.Error())
return
}
// Resource groups are still being loaded asynchronously: the request can
// succeed once loading completes, so report it as retryable rather than as
// an internal error.
if errs.ErrResourceGroupsLoading.Equal(err) {
c.String(http.StatusServiceUnavailable, err.Error())
return
}
c.String(http.StatusInternalServerError, err.Error())
}

Expand All @@ -525,5 +532,9 @@ func (*ConfigService) respondStoreWriteError(c *gin.Context, err error) {
c.String(http.StatusNotFound, err.Error())
return
}
if errs.ErrResourceGroupsLoading.Equal(err) {
c.String(http.StatusServiceUnavailable, err.Error())
return
}
c.String(http.StatusInternalServerError, err.Error())
}
26 changes: 25 additions & 1 deletion pkg/mcs/resourcemanager/metadataapi/config_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,26 @@ func TestConfigServiceGroupCRUDAndErrorCodes(t *testing.T) {
}
}

// TestConfigServiceLoadingReturns503 asserts the "resource groups are still
// being loaded" error is reported as retryable. It is a transient startup state,
// not an internal error, so callers and load balancers must be able to tell the
// difference and retry.
func TestConfigServiceLoadingReturns503(t *testing.T) {
t.Parallel()

re := require.New(t)
store := newTestStore()
handler := newTestHTTPHandler(store)

store.listErr = pderrors.ErrResourceGroupsLoading
resp := doJSONRequest(re, handler, http.MethodGet, "/resource-manager/api/v1/config/groups", nil)
re.Equal(http.StatusServiceUnavailable, resp.Code)

store.listErr = errors.New("boom")
resp = doJSONRequest(re, handler, http.MethodGet, "/resource-manager/api/v1/config/groups", nil)
re.Equal(http.StatusInternalServerError, resp.Code)
}

func TestConfigServiceControllerAllOrNothing(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -351,6 +371,7 @@ type testStore struct {
serviceLimits map[uint32]float64
addErr error
setServiceLimitErr error
listErr error
updatedControllerConfigItems []string
}

Expand Down Expand Up @@ -406,7 +427,10 @@ func (s *testStore) GetResourceGroup(keyspaceID uint32, name string, withStats b
return group.Clone(withStats), nil
}

func (*testStore) GetResourceGroupList(_ uint32, _ bool) ([]*rmserver.ResourceGroup, error) {
func (s *testStore) GetResourceGroupList(_ uint32, _ bool) ([]*rmserver.ResourceGroup, error) {
if s.listErr != nil {
return nil, s.listErr
}
return []*rmserver.ResourceGroup{}, nil
}

Expand Down
29 changes: 18 additions & 11 deletions pkg/mcs/resourcemanager/server/grpc_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (s *Service) GetResourceGroup(_ context.Context, req *rmpb.GetResourceGroup
keyspaceID := ExtractKeyspaceID(req.GetKeyspaceId())
rg, err := s.manager.GetResourceGroup(keyspaceID, req.ResourceGroupName, req.WithRuStats)
if err != nil {
return nil, err
return nil, errs.ErrResourceGroupsLoadingGRPC(err)
}
if rg == nil {
return nil, errs.ErrResourceGroupNotExists.FastGenByArgs(req.ResourceGroupName)
Expand All @@ -129,7 +129,7 @@ func (s *Service) ListResourceGroups(_ context.Context, req *rmpb.ListResourceGr
keyspaceID := ExtractKeyspaceID(req.GetKeyspaceId())
groups, err := s.manager.GetResourceGroupList(keyspaceID, req.WithRuStats)
if err != nil {
return nil, err
return nil, errs.ErrResourceGroupsLoadingGRPC(err)
}
resps := &rmpb.ListResourceGroupsResponse{
Groups: make([]*rmpb.ResourceGroup, 0, len(groups)),
Expand All @@ -151,7 +151,7 @@ func (s *Service) AddResourceGroup(_ context.Context, req *rmpb.PutResourceGroup
}
err := s.manager.AddResourceGroup(req.GetGroup())
if err != nil {
return nil, err
return nil, errs.ErrResourceGroupsLoadingGRPC(err)
}
return &rmpb.PutResourceGroupResponse{Body: "Success!"}, nil
}
Expand All @@ -166,7 +166,7 @@ func (s *Service) DeleteResourceGroup(_ context.Context, req *rmpb.DeleteResourc
}
err := s.manager.DeleteResourceGroup(ExtractKeyspaceID(req.GetKeyspaceId()), req.ResourceGroupName)
if err != nil {
return nil, err
return nil, errs.ErrResourceGroupsLoadingGRPC(err)
}
return &rmpb.DeleteResourceGroupResponse{Body: "Success!"}, nil
}
Expand All @@ -181,7 +181,7 @@ func (s *Service) ModifyResourceGroup(_ context.Context, req *rmpb.PutResourceGr
}
err := s.manager.ModifyResourceGroup(req.GetGroup())
if err != nil {
return nil, err
return nil, errs.ErrResourceGroupsLoadingGRPC(err)
}
return &rmpb.PutResourceGroupResponse{Body: "Success!"}, nil
}
Expand Down Expand Up @@ -230,18 +230,25 @@ func (s *Service) AcquireTokenBuckets(stream rmpb.ResourceManager_AcquireTokenBu
zap.Uint32("keyspace-id", keyspaceID),
zap.String("resource-group", resourceGroupName),
)
// Get the resource group from manager to acquire token buckets. This also
// triggers lazy loading of the group if async loading hasn't completed yet,
// so it must happen before accessKeyspaceResourceGroupManager below.
// Lazy loading can fail transiently (a storage read error, or retries
// exhausted while async loading is still in progress). Skip just this
// request instead of returning: the error belongs to one resource group,
// while returning would tear down the whole stream and force every
// client multiplexed on it to reconnect.
rg, err := s.manager.GetMutableResourceGroup(keyspaceID, resourceGroupName)
if rg == nil {
log.Warn("resource group is unavailable", append(requestFields, zap.Error(err))...)
continue
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
// Get keyspace resource group manager to apply service limit later.
krgm, err := s.manager.accessKeyspaceResourceGroupManager(keyspaceID, resourceGroupName)
if krgm == nil {
log.Warn("keyspace resource group manager not found", append(requestFields, zap.Error(err))...)
continue
}
// Get the resource group from manager to acquire token buckets.
rg, err := s.manager.GetMutableResourceGroup(keyspaceID, resourceGroupName)
if rg == nil {
log.Warn("resource group not found", append(requestFields, zap.Error(err))...)
continue
}
// Send the consumption to update the metrics.
err = s.manager.dispatchConsumption(req)
if err != nil {
Expand Down
Loading
Loading