@@ -218,6 +218,76 @@ void TBlobStorageController::Handle(TEvents::TEvUndelivered::TPtr ev) {
218218 }
219219}
220220
221+ bool TBlobStorageController::HostConfigEquals (const THostConfigInfo& left, const NKikimrBlobStorage::TDefineHostConfig& right) const {
222+ if (left.Name != right.GetName ()) {
223+ return false ;
224+ }
225+
226+ THashMap<TStringBuf, const THostConfigInfo::TDriveInfo*> driveMap;
227+ for (const auto & [key, info] : left.Drives ) {
228+ driveMap.emplace (key.Path , &info);
229+ }
230+
231+ TMaybe<TString> defaultPDiskConfig;
232+ if (right.HasDefaultHostPDiskConfig ()) {
233+ const bool success = right.GetDefaultHostPDiskConfig ().SerializeToString (&defaultPDiskConfig.ConstructInPlace ());
234+ Y_ABORT_UNLESS (success);
235+ }
236+
237+ auto checkDrive = [&](const auto & drive) {
238+ const auto it = driveMap.find (drive.GetPath ());
239+ if (it == driveMap.end ()) {
240+ return false ;
241+ }
242+
243+ if (drive.GetType () != it->second ->Type ||
244+ drive.GetSharedWithOs () != it->second ->SharedWithOs ||
245+ drive.GetReadCentric () != it->second ->ReadCentric ||
246+ drive.GetKind () != it->second ->Kind ) {
247+ return false ;
248+ }
249+
250+ TMaybe<TString> pdiskConfig;
251+ if (drive.HasPDiskConfig ()) {
252+ const bool success = drive.GetPDiskConfig ().SerializeToString (&pdiskConfig.ConstructInPlace ());
253+ Y_ABORT_UNLESS (success);
254+ } else {
255+ pdiskConfig = defaultPDiskConfig;
256+ }
257+
258+ if (pdiskConfig != it->second ->PDiskConfig ) {
259+ return false ;
260+ }
261+
262+ driveMap.erase (it);
263+ return true ;
264+ };
265+
266+ for (const auto & drive : right.GetDrive ()) {
267+ if (!checkDrive (drive)) {
268+ return false ;
269+ }
270+ }
271+
272+ auto addDrives = [&](const auto & field, NKikimrBlobStorage::EPDiskType type) {
273+ NKikimrBlobStorage::THostConfigDrive drive;
274+ drive.SetType (type);
275+ for (const auto & path : field) {
276+ if (drive.SetPath (path); !checkDrive (drive)) {
277+ return false ;
278+ }
279+ }
280+ return true ;
281+ };
282+ if (!addDrives (right.GetRot (), NKikimrBlobStorage::EPDiskType::ROT) ||
283+ !addDrives (right.GetSsd (), NKikimrBlobStorage::EPDiskType::SSD) ||
284+ !addDrives (right.GetNvme (), NKikimrBlobStorage::EPDiskType::NVME)) {
285+ return false ;
286+ }
287+
288+ return driveMap.empty ();
289+ }
290+
221291void TBlobStorageController::ApplyStorageConfig (bool ignoreDistconf) {
222292 if (!StorageConfig.HasBlobStorageConfig ()) {
223293 return ;
@@ -232,74 +302,101 @@ void TBlobStorageController::ApplyStorageConfig(bool ignoreDistconf) {
232302 return ; // not expected to be managed by BSC
233303 }
234304
305+ ui64 expectedBoxId;
235306 std::optional<ui64> generation;
307+ bool needToDefineBox = true ;
236308 if (!Boxes.empty ()) {
237309 const auto & [boxId, box] = *Boxes.begin ();
238310
239- THashSet<THostConfigId> unusedHostConfigs;
240- for (const auto & [hostConfigId, _] : HostConfigs) {
241- unusedHostConfigs.insert (hostConfigId);
311+ expectedBoxId = boxId;
312+ generation = box.Generation .GetOrElse (1 );
313+ needToDefineBox = false ;
314+
315+ // put all existing hosts of a singular box into the set
316+ THashSet<std::tuple<TString, ui32, ui64, TMaybe<ui32>>> hosts;
317+ for (const auto & [key, value] : box.Hosts ) {
318+ hosts.emplace (key.Fqdn , key.IcPort , value.HostConfigId , value.EnforcedNodeId );
242319 }
243- for (const auto & [_, host] : box.Hosts ) {
244- if (!HostConfigs.contains (host.HostConfigId )) {
245- return ;
320+
321+ // drop matching entries from the new set
322+ for (const auto & host : bsConfig.GetDefineBox ().GetHost ()) {
323+ const auto & resolved = HostRecords->GetHostId (host.GetEnforcedNodeId ());
324+ Y_ABORT_UNLESS (resolved);
325+ const auto & [fqdn, port] = *resolved;
326+
327+ if (!hosts.erase (std::make_tuple (fqdn, port, host.GetHostConfigId (), Nothing ()))) {
328+ needToDefineBox = true ;
329+ break ;
246330 }
247- unusedHostConfigs.erase (host.HostConfigId );
248331 }
249332
250- if (!unusedHostConfigs .empty ()) {
251- return ;
333+ if (!hosts .empty ()) {
334+ needToDefineBox = true ;
252335 }
253-
254- generation = box.Generation .GetOrElse (0 );
255336 }
256337
257338 auto ev = std::make_unique<TEvBlobStorage::TEvControllerConfigRequest>();
258339 auto & r = ev->Record ;
259340 auto *request = r.MutableRequest ();
260341 for (const auto & hostConfig : bsConfig.GetDefineHostConfig ()) {
342+ const auto it = HostConfigs.find (hostConfig.GetHostConfigId ());
343+ if (it != HostConfigs.end () && HostConfigEquals (it->second , hostConfig)) {
344+ continue ;
345+ }
346+
347+ auto *cmd = request->AddCommand ();
348+ auto *defineHostConfig = cmd->MutableDefineHostConfig ();
349+ defineHostConfig->CopyFrom (hostConfig);
350+ if (it != HostConfigs.end ()) {
351+ defineHostConfig->SetItemConfigGeneration (it->second .Generation .GetOrElse (1 ));
352+ }
353+ }
354+
355+ if (needToDefineBox) {
261356 auto *cmd = request->AddCommand ();
262- cmd->MutableDefineHostConfig ()->CopyFrom (hostConfig);
263- }
264- auto *cmd = request->AddCommand ();
265- auto *defineBox = cmd->MutableDefineBox ();
266- defineBox->CopyFrom (bsConfig.GetDefineBox ());
267- defineBox->SetBoxId (1 );
268- for (auto & host : *defineBox->MutableHost ()) {
269- const ui32 nodeId = host.GetEnforcedNodeId ();
270- host.ClearEnforcedNodeId ();
271- auto *key = host.MutableKey ();
272- const auto & resolved = HostRecords->GetHostId (nodeId);
273- Y_ABORT_UNLESS (resolved);
274- const auto & [fqdn, port] = *resolved;
275- key->SetFqdn (fqdn);
276- key->SetIcPort (port);
277- }
278- if (generation) {
279- defineBox->SetItemConfigGeneration (*generation);
280- }
281-
282- THashSet<THostConfigId> unusedHostConfigs;
283- for (const auto & [hostConfigId, _] : HostConfigs) {
284- unusedHostConfigs.insert (hostConfigId);
285- }
286- for (const auto & host : defineBox->GetHost ()) {
287- unusedHostConfigs.erase (host.GetHostConfigId ());
288- }
289- for (const THostConfigId hostConfigId : unusedHostConfigs) {
357+ auto *defineBox = cmd->MutableDefineBox ();
358+ defineBox->CopyFrom (bsConfig.GetDefineBox ());
359+ defineBox->SetBoxId (expectedBoxId);
360+ for (auto & host : *defineBox->MutableHost ()) {
361+ const ui32 nodeId = host.GetEnforcedNodeId ();
362+ host.ClearEnforcedNodeId ();
363+ auto *key = host.MutableKey ();
364+ const auto & resolved = HostRecords->GetHostId (nodeId);
365+ Y_ABORT_UNLESS (resolved);
366+ const auto & [fqdn, port] = *resolved;
367+ key->SetFqdn (fqdn);
368+ key->SetIcPort (port);
369+ }
370+ if (generation) {
371+ defineBox->SetItemConfigGeneration (*generation);
372+ }
373+ }
374+
375+ THashMap<THostConfigId, ui64> unusedHostConfigs;
376+ for (const auto & [hostConfigId, value] : HostConfigs) {
377+ unusedHostConfigs.emplace (hostConfigId, value.Generation .GetOrElse (1 ));
378+ }
379+ for (const auto & hostConfig : bsConfig.GetDefineHostConfig ()) {
380+ unusedHostConfigs.erase (hostConfig.GetHostConfigId ());
381+ }
382+ for (const auto & [hostConfigId, generation] : unusedHostConfigs) {
290383 auto *cmd = request->AddCommand ();
291384 auto *del = cmd->MutableDeleteHostConfig ();
292385 del->SetHostConfigId (hostConfigId);
293- del->SetItemConfigGeneration (HostConfigs[hostConfigId]. Generation . GetOrElse ( 0 ) );
386+ del->SetItemConfigGeneration (generation );
294387 }
295388
296- STLOG (PRI_DEBUG, BS_CONTROLLER, BSC14, " ApplyStorageConfig" , (Request, r));
297-
298- Send (SelfId (), ev.release ());
389+ if (request->CommandSize ()) {
390+ STLOG (PRI_DEBUG, BS_CONTROLLER, BSC14, " ApplyStorageConfig" , (Request, r));
391+ Send (SelfId (), ev.release ());
392+ }
299393}
300394
301395void TBlobStorageController::Handle (TEvBlobStorage::TEvControllerConfigResponse::TPtr ev) {
302- STLOG (PRI_DEBUG, BS_CONTROLLER, BSC15, " TEvControllerConfigResponse" , (Response, ev->Get ()->Record ));
396+ auto & record = ev->Get ()->Record ;
397+ auto & response = record.GetResponse ();
398+ STLOG (response.GetSuccess () ? PRI_DEBUG : PRI_ERROR, BS_CONTROLLER, BSC15, " TEvControllerConfigResponse" ,
399+ (Response, response));
303400}
304401
305402void TBlobStorageController::Handle (TEvBlobStorage::TEvControllerUpdateGroupStat::TPtr& ev) {
0 commit comments