@@ -197,6 +197,76 @@ void TBlobStorageController::Handle(TEvents::TEvUndelivered::TPtr ev) {
197197 }
198198}
199199
200+ bool TBlobStorageController::HostConfigEquals (const THostConfigInfo& left, const NKikimrBlobStorage::TDefineHostConfig& right) const {
201+ if (left.Name != right.GetName ()) {
202+ return false ;
203+ }
204+
205+ THashMap<TStringBuf, const THostConfigInfo::TDriveInfo*> driveMap;
206+ for (const auto & [key, info] : left.Drives ) {
207+ driveMap.emplace (key.Path , &info);
208+ }
209+
210+ TMaybe<TString> defaultPDiskConfig;
211+ if (right.HasDefaultHostPDiskConfig ()) {
212+ const bool success = right.GetDefaultHostPDiskConfig ().SerializeToString (&defaultPDiskConfig.ConstructInPlace ());
213+ Y_ABORT_UNLESS (success);
214+ }
215+
216+ auto checkDrive = [&](const auto & drive) {
217+ const auto it = driveMap.find (drive.GetPath ());
218+ if (it == driveMap.end ()) {
219+ return false ;
220+ }
221+
222+ if (drive.GetType () != it->second ->Type ||
223+ drive.GetSharedWithOs () != it->second ->SharedWithOs ||
224+ drive.GetReadCentric () != it->second ->ReadCentric ||
225+ drive.GetKind () != it->second ->Kind ) {
226+ return false ;
227+ }
228+
229+ TMaybe<TString> pdiskConfig;
230+ if (drive.HasPDiskConfig ()) {
231+ const bool success = drive.GetPDiskConfig ().SerializeToString (&pdiskConfig.ConstructInPlace ());
232+ Y_ABORT_UNLESS (success);
233+ } else {
234+ pdiskConfig = defaultPDiskConfig;
235+ }
236+
237+ if (pdiskConfig != it->second ->PDiskConfig ) {
238+ return false ;
239+ }
240+
241+ driveMap.erase (it);
242+ return true ;
243+ };
244+
245+ for (const auto & drive : right.GetDrive ()) {
246+ if (!checkDrive (drive)) {
247+ return false ;
248+ }
249+ }
250+
251+ auto addDrives = [&](const auto & field, NKikimrBlobStorage::EPDiskType type) {
252+ NKikimrBlobStorage::THostConfigDrive drive;
253+ drive.SetType (type);
254+ for (const auto & path : field) {
255+ if (drive.SetPath (path); !checkDrive (drive)) {
256+ return false ;
257+ }
258+ }
259+ return true ;
260+ };
261+ if (!addDrives (right.GetRot (), NKikimrBlobStorage::EPDiskType::ROT) ||
262+ !addDrives (right.GetSsd (), NKikimrBlobStorage::EPDiskType::SSD) ||
263+ !addDrives (right.GetNvme (), NKikimrBlobStorage::EPDiskType::NVME)) {
264+ return false ;
265+ }
266+
267+ return driveMap.empty ();
268+ }
269+
200270void TBlobStorageController::ApplyStorageConfig (bool ignoreDistconf) {
201271 if (!StorageConfig.HasBlobStorageConfig ()) {
202272 return ;
@@ -211,74 +281,101 @@ void TBlobStorageController::ApplyStorageConfig(bool ignoreDistconf) {
211281 return ; // not expected to be managed by BSC
212282 }
213283
284+ ui64 expectedBoxId;
214285 std::optional<ui64> generation;
286+ bool needToDefineBox = true ;
215287 if (!Boxes.empty ()) {
216288 const auto & [boxId, box] = *Boxes.begin ();
217289
218- THashSet<THostConfigId> unusedHostConfigs;
219- for (const auto & [hostConfigId, _] : HostConfigs) {
220- unusedHostConfigs.insert (hostConfigId);
290+ expectedBoxId = boxId;
291+ generation = box.Generation .GetOrElse (1 );
292+ needToDefineBox = false ;
293+
294+ // put all existing hosts of a singular box into the set
295+ THashSet<std::tuple<TString, ui32, ui64, TMaybe<ui32>>> hosts;
296+ for (const auto & [key, value] : box.Hosts ) {
297+ hosts.emplace (key.Fqdn , key.IcPort , value.HostConfigId , value.EnforcedNodeId );
221298 }
222- for (const auto & [_, host] : box.Hosts ) {
223- if (!HostConfigs.contains (host.HostConfigId )) {
224- return ;
299+
300+ // drop matching entries from the new set
301+ for (const auto & host : bsConfig.GetDefineBox ().GetHost ()) {
302+ const auto & resolved = HostRecords->GetHostId (host.GetEnforcedNodeId ());
303+ Y_ABORT_UNLESS (resolved);
304+ const auto & [fqdn, port] = *resolved;
305+
306+ if (!hosts.erase (std::make_tuple (fqdn, port, host.GetHostConfigId (), Nothing ()))) {
307+ needToDefineBox = true ;
308+ break ;
225309 }
226- unusedHostConfigs.erase (host.HostConfigId );
227310 }
228311
229- if (!unusedHostConfigs .empty ()) {
230- return ;
312+ if (!hosts .empty ()) {
313+ needToDefineBox = true ;
231314 }
232-
233- generation = box.Generation .GetOrElse (0 );
234315 }
235316
236317 auto ev = std::make_unique<TEvBlobStorage::TEvControllerConfigRequest>();
237318 auto & r = ev->Record ;
238319 auto *request = r.MutableRequest ();
239320 for (const auto & hostConfig : bsConfig.GetDefineHostConfig ()) {
321+ const auto it = HostConfigs.find (hostConfig.GetHostConfigId ());
322+ if (it != HostConfigs.end () && HostConfigEquals (it->second , hostConfig)) {
323+ continue ;
324+ }
325+
326+ auto *cmd = request->AddCommand ();
327+ auto *defineHostConfig = cmd->MutableDefineHostConfig ();
328+ defineHostConfig->CopyFrom (hostConfig);
329+ if (it != HostConfigs.end ()) {
330+ defineHostConfig->SetItemConfigGeneration (it->second .Generation .GetOrElse (1 ));
331+ }
332+ }
333+
334+ if (needToDefineBox) {
240335 auto *cmd = request->AddCommand ();
241- cmd->MutableDefineHostConfig ()->CopyFrom (hostConfig);
242- }
243- auto *cmd = request->AddCommand ();
244- auto *defineBox = cmd->MutableDefineBox ();
245- defineBox->CopyFrom (bsConfig.GetDefineBox ());
246- defineBox->SetBoxId (1 );
247- for (auto & host : *defineBox->MutableHost ()) {
248- const ui32 nodeId = host.GetEnforcedNodeId ();
249- host.ClearEnforcedNodeId ();
250- auto *key = host.MutableKey ();
251- const auto & resolved = HostRecords->GetHostId (nodeId);
252- Y_ABORT_UNLESS (resolved);
253- const auto & [fqdn, port] = *resolved;
254- key->SetFqdn (fqdn);
255- key->SetIcPort (port);
256- }
257- if (generation) {
258- defineBox->SetItemConfigGeneration (*generation);
259- }
260-
261- THashSet<THostConfigId> unusedHostConfigs;
262- for (const auto & [hostConfigId, _] : HostConfigs) {
263- unusedHostConfigs.insert (hostConfigId);
264- }
265- for (const auto & host : defineBox->GetHost ()) {
266- unusedHostConfigs.erase (host.GetHostConfigId ());
267- }
268- for (const THostConfigId hostConfigId : unusedHostConfigs) {
336+ auto *defineBox = cmd->MutableDefineBox ();
337+ defineBox->CopyFrom (bsConfig.GetDefineBox ());
338+ defineBox->SetBoxId (expectedBoxId);
339+ for (auto & host : *defineBox->MutableHost ()) {
340+ const ui32 nodeId = host.GetEnforcedNodeId ();
341+ host.ClearEnforcedNodeId ();
342+ auto *key = host.MutableKey ();
343+ const auto & resolved = HostRecords->GetHostId (nodeId);
344+ Y_ABORT_UNLESS (resolved);
345+ const auto & [fqdn, port] = *resolved;
346+ key->SetFqdn (fqdn);
347+ key->SetIcPort (port);
348+ }
349+ if (generation) {
350+ defineBox->SetItemConfigGeneration (*generation);
351+ }
352+ }
353+
354+ THashMap<THostConfigId, ui64> unusedHostConfigs;
355+ for (const auto & [hostConfigId, value] : HostConfigs) {
356+ unusedHostConfigs.emplace (hostConfigId, value.Generation .GetOrElse (1 ));
357+ }
358+ for (const auto & hostConfig : bsConfig.GetDefineHostConfig ()) {
359+ unusedHostConfigs.erase (hostConfig.GetHostConfigId ());
360+ }
361+ for (const auto & [hostConfigId, generation] : unusedHostConfigs) {
269362 auto *cmd = request->AddCommand ();
270363 auto *del = cmd->MutableDeleteHostConfig ();
271364 del->SetHostConfigId (hostConfigId);
272- del->SetItemConfigGeneration (HostConfigs[hostConfigId]. Generation . GetOrElse ( 0 ) );
365+ del->SetItemConfigGeneration (generation );
273366 }
274367
275- STLOG (PRI_DEBUG, BS_CONTROLLER, BSC14, " ApplyStorageConfig" , (Request, r));
276-
277- Send (SelfId (), ev.release ());
368+ if (request->CommandSize ()) {
369+ STLOG (PRI_DEBUG, BS_CONTROLLER, BSC14, " ApplyStorageConfig" , (Request, r));
370+ Send (SelfId (), ev.release ());
371+ }
278372}
279373
280374void TBlobStorageController::Handle (TEvBlobStorage::TEvControllerConfigResponse::TPtr ev) {
281- STLOG (PRI_DEBUG, BS_CONTROLLER, BSC15, " TEvControllerConfigResponse" , (Response, ev->Get ()->Record ));
375+ auto & record = ev->Get ()->Record ;
376+ auto & response = record.GetResponse ();
377+ STLOG (response.GetSuccess () ? PRI_DEBUG : PRI_ERROR, BS_CONTROLLER, BSC15, " TEvControllerConfigResponse" ,
378+ (Response, response));
282379}
283380
284381void TBlobStorageController::Handle (TEvBlobStorage::TEvControllerUpdateGroupStat::TPtr& ev) {
0 commit comments