@@ -293,7 +293,7 @@ namespace NKikimr::NBsController {
293293
294294 bool TBlobStorageController::CommitConfigUpdates (TConfigState& state, bool suppressFailModelChecking,
295295 bool suppressDegradedGroupsChecking, bool suppressDisintegratedGroupsChecking,
296- TTransactionContext& txc, TString *errorDescription) {
296+ TTransactionContext& txc, TString *errorDescription, NKikimrBlobStorage::TConfigResponse *response ) {
297297 NIceDb::TNiceDb db (txc.DB );
298298
299299 for (TGroupId groupId : state.GroupContentChanged ) {
@@ -309,16 +309,20 @@ namespace NKikimr::NBsController {
309309 }
310310 }
311311
312+ bool errors = false ;
313+ std::vector<TGroupId> disintegratedByExpectedStatus;
314+ std::vector<TGroupId> disintegrated;
315+ std::vector<TGroupId> degraded;
316+
312317 if (!suppressDisintegratedGroupsChecking) {
313318 for (auto && [base, overlay] : state.Groups .Diff ()) {
314319 if (base && overlay->second ) {
315320 const TGroupInfo::TGroupStatus& prev = base->second ->Status ;
316321 const TGroupInfo::TGroupStatus& status = overlay->second ->Status ;
317322 if (status.ExpectedStatus == NKikimrBlobStorage::TGroupStatus::DISINTEGRATED &&
318323 status.ExpectedStatus != prev.ExpectedStatus ) { // status did really change
319- *errorDescription = TStringBuilder () << " GroupId# " << overlay->first
320- << " ExpectedStatus# DISINTEGRATED" ;
321- return false ;
324+ disintegratedByExpectedStatus.push_back (overlay->first );
325+ errors = true ;
322326 }
323327 }
324328 }
@@ -340,20 +344,44 @@ namespace NKikimr::NBsController {
340344 // check the failure model
341345 auto & checker = *topology.QuorumChecker ;
342346 if (!checker.CheckFailModelForGroup (failed)) {
343- *errorDescription = TStringBuilder () << " GroupId# " << groupId
344- << " may lose data while modifying group" ;
345- return false ;
347+ disintegrated.push_back (groupId);
348+ errors = true ;
346349 } else if (!suppressDegradedGroupsChecking && checker.IsDegraded (failed)) {
347- *errorDescription = TStringBuilder () << " GroupId# " << groupId
348- << " may become DEGRADED while modifying group" ;
349- return false ;
350+ degraded.push_back (groupId);
351+ errors = true ;
350352 }
351353 } else {
352354 Y_ABORT_UNLESS (group); // group must exist
353355 }
354356 }
355357 }
356358
359+ if (errors) {
360+ TStringStream msg;
361+ if (!degraded.empty ()) {
362+ msg << " Degraded GroupIds# " << FormatList (degraded) << ' ' ;
363+ if (response) {
364+ response->MutableGroupsGetDegraded ()->Add (degraded.begin (), degraded.end ());
365+ }
366+ }
367+ if (!disintegrated.empty ()) {
368+ msg << " Disintegrated GroupIds# " << FormatList (disintegrated) << ' ' ;
369+ if (response) {
370+ response->MutableGroupsGetDisintegrated ()->Add (disintegrated.begin (), disintegrated.end ());
371+ }
372+ }
373+ if (!disintegratedByExpectedStatus.empty ()) {
374+ msg << " DisintegratedByExpectedStatus GroupIds# " << FormatList (disintegratedByExpectedStatus) << ' ' ;
375+ if (response) {
376+ response->MutableGroupsGetDisintegratedByExpectedStatus ()->Add (disintegratedByExpectedStatus.begin (),
377+ disintegratedByExpectedStatus.end ());
378+ }
379+ }
380+ *errorDescription = msg.Str ();
381+ errorDescription->pop_back ();
382+ return false ;
383+ }
384+
357385 // trim PDisks awaiting deletion
358386 for (const TPDiskId& pdiskId : state.PDisksToRemove ) {
359387 TPDiskInfo *pdiskInfo = state.PDisks .FindForUpdate (pdiskId);
0 commit comments