config_fit_pdisks.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. #include "config.h"
  2. #include <util/generic/string.h>
  3. #include <util/system/types.h>
  4. namespace NKikimr {
  5. namespace NBsController {
  6. struct TDiskId {
  7. ui32 NodeId = 0;
  8. TString Path;
  9. bool operator==(const TDiskId& other) const {
  10. return NodeId == other.NodeId && Path == other.Path;
  11. }
  12. };
  13. struct TDiskInfo {
  14. ui32 NodeId = 0;
  15. TBlobStorageController::THostId HostId = {};
  16. TBoxId BoxId = 0;
  17. TString Path;
  18. TString LastSeenPath;
  19. TString Serial;
  20. TString LastSeenSerial;
  21. bool SharedWithOs = false;
  22. bool ReadCentric = false;
  23. TPDiskCategory PDiskCategory = {};
  24. TString PDiskConfig;
  25. TDiskId GetId() const {
  26. return {NodeId, Path};
  27. }
  28. };
  29. } // NBsController
  30. } // NKikimr
  31. namespace std {
  32. template <>
  33. struct hash<NKikimr::NBsController::TDiskId> {
  34. size_t operator()(const NKikimr::NBsController::TDiskId& diskId) const {
  35. return hash<ui32>()(diskId.NodeId) ^ hash<TString>()(diskId.Path);
  36. }
  37. };
  38. }
  39. namespace NKikimr {
  40. namespace NBsController {
  41. static TPDiskId FindFirstEmptyPDiskId(const TOverlayMap<TPDiskId, TBlobStorageController::TPDiskInfo>& pdisks,
  42. TNodeId nodeId) {
  43. Schema::PDisk::PDiskID::Type nextPDiskID = 1000; // start allocation from this number
  44. // generate PDisk id; skip generated one if it already exists (e.g. user has added
  45. // such PDisk by hand)
  46. TPDiskId pdiskId;
  47. do {
  48. pdiskId = TPDiskId(nodeId, nextPDiskID++); // postincrement this number
  49. } while (pdisks.Find(pdiskId));
  50. return pdiskId;
  51. }
  52. static TString FormatPDiskConfig(const TString& s) {
  53. NKikimrBlobStorage::TPDiskConfig proto;
  54. return proto.ParseFromString(s) ? SingleLineProto(proto) : "<error>";
  55. }
  56. static std::optional<TPDiskId> FindPDisk(const TDiskInfo& disk, const TBlobStorageController::TConfigState& state) {
  57. auto id = state.FindPDiskByLocation(disk.NodeId, disk.Path);
  58. if (!id) {
  59. id = state.FindPDiskByLocation(disk.NodeId, disk.Serial);
  60. }
  61. return id;
  62. }
  63. static std::optional<TPDiskId> FindStaticPDisk(const TDiskInfo& disk, const TBlobStorageController::TConfigState& state) {
  64. auto id = state.FindStaticPDiskByLocation(disk.NodeId, disk.Path);
  65. if (!id) {
  66. id = state.FindStaticPDiskByLocation(disk.NodeId, disk.Serial);
  67. }
  68. return id;
  69. }
  70. static void UpdatePDiskIfNeeded(const TPDiskId& pdiskId, const TDiskInfo& disk, ui32 defaultMaxSlots, TBlobStorageController::TConfigState& state) {
  71. auto pdiskInfo = state.PDisks.Find(pdiskId);
  72. Y_VERIFY(pdiskInfo != nullptr);
  73. if (pdiskInfo->Kind != disk.PDiskCategory ||
  74. pdiskInfo->SharedWithOs != disk.SharedWithOs ||
  75. pdiskInfo->ReadCentric != disk.ReadCentric ||
  76. pdiskInfo->BoxId != disk.BoxId ||
  77. pdiskInfo->PDiskConfig != disk.PDiskConfig)
  78. {
  79. // update PDisk configuration
  80. auto pdiskInfo = state.PDisks.FindForUpdate(pdiskId);
  81. Y_VERIFY(pdiskInfo != nullptr);
  82. pdiskInfo->Kind = disk.PDiskCategory;
  83. pdiskInfo->SharedWithOs = disk.SharedWithOs;
  84. pdiskInfo->ReadCentric = disk.ReadCentric;
  85. pdiskInfo->BoxId = disk.BoxId;
  86. if (pdiskInfo->PDiskConfig != disk.PDiskConfig) {
  87. if (const auto id = FindStaticPDisk(disk, state); id && state.StaticPDisks.at(*id).PDiskConfig != disk.PDiskConfig) {
  88. throw TExError() << "PDiskConfig mismatch for static disk" << TErrorParams::NodeId(disk.NodeId) << TErrorParams::Path(disk.Path);
  89. } else {
  90. pdiskInfo->PDiskConfig = disk.PDiskConfig;
  91. }
  92. }
  93. // run ExtractConfig as the very last step
  94. pdiskInfo->ExtractConfig(defaultMaxSlots);
  95. }
  96. }
  97. // return TString not const TString& to make sure we never use dangling reference
  98. static TString GetDiskPathFromNode(ui32 nodeId, const TString& serialNumber, const TBlobStorageController::TConfigState& state, bool throwOnError = false) {
  99. if (auto nodeIt = state.Nodes.Get().find(nodeId); nodeIt != state.Nodes.Get().end()) {
  100. for (const auto& [_, driveData] : nodeIt->second.KnownDrives) {
  101. if (serialNumber == driveData.SerialNumber) {
  102. return driveData.Path;
  103. }
  104. }
  105. if (throwOnError) {
  106. throw TExError() << "Couldn't find disk's path by serial number " << TErrorParams::DiskSerialNumber(serialNumber);
  107. }
  108. } else {
  109. if (throwOnError) {
  110. throw TExError() << "Unknown node id " << TErrorParams::NodeId(nodeId);
  111. }
  112. }
  113. return TString();
  114. }
  115. // return TString not const TString& to make sure we never use dangling reference
  116. static TString GetDiskSerialNumberFromNode(ui32 nodeId, const TString& path, const TBlobStorageController::TConfigState& state, bool throwOnError = false) {
  117. if (auto nodeIt = state.Nodes.Get().find(nodeId); nodeIt != state.Nodes.Get().end()) {
  118. for (const auto& [_, driveData] : nodeIt->second.KnownDrives) {
  119. if (path == driveData.Path) {
  120. return driveData.SerialNumber;
  121. }
  122. }
  123. if (throwOnError) {
  124. throw TExError() << "Couldn't find disk's serial number by path " << TErrorParams::Path(path);
  125. }
  126. } else {
  127. if (throwOnError) {
  128. throw TExError() << "Unknown node id " << TErrorParams::NodeId(nodeId);
  129. }
  130. }
  131. return TString();
  132. }
  133. static std::unordered_map<TDiskId, TDiskInfo> GetDisksFromHostConfig(const TBlobStorageController::TConfigState& state, const std::set<TBoxId>& relevantBoxes) {
  134. std::unordered_map<TDiskId, TDiskInfo> disks;
  135. const auto& hostConfigs = state.HostConfigs.Get();
  136. const auto& boxes = state.Boxes.Get();
  137. for (const TBoxId& boxId : relevantBoxes) {
  138. const auto boxIt = boxes.find(boxId);
  139. if (boxIt == boxes.end()) {
  140. continue; // box was deleted
  141. }
  142. const auto& box = boxIt->second;
  143. THashSet<TNodeId> usedNodes;
  144. for (const auto& [hostKey, hostValue] : box.Hosts) {
  145. const auto& hostConfigId = hostValue.HostConfigId;
  146. auto it = hostConfigs.find(hostConfigId);
  147. if (it == hostConfigs.end()) {
  148. throw TExHostConfigNotFound(hostConfigId);
  149. }
  150. const auto& hostConfig = it->second;
  151. const TBlobStorageController::THostId hostId(hostKey.Fqdn, hostKey.IcPort);
  152. const auto& nodeId = state.HostRecords->ResolveNodeId(hostKey, hostValue);
  153. if (!nodeId) {
  154. throw TExHostNotFound(hostKey) << TErrorParams::BoxId(boxId) << TErrorParams::NodeId(*nodeId);
  155. } else if (!usedNodes.insert(*nodeId).second) {
  156. throw TExError() << "duplicate NodeId" << TErrorParams::BoxId(boxId) << TErrorParams::NodeId(*nodeId)
  157. << TErrorParams::Fqdn(hostKey.Fqdn) << TErrorParams::IcPort(hostKey.IcPort);
  158. }
  159. for (const auto& [drive, driveInfo] : hostConfig.Drives) {
  160. auto serial = GetDiskSerialNumberFromNode(*nodeId, drive.Path, state, /* throwOnError */ false);
  161. TDiskInfo disk;
  162. disk.BoxId = boxId;
  163. disk.HostId = hostId;
  164. disk.LastSeenPath = TString();
  165. disk.LastSeenSerial = serial;
  166. disk.NodeId = *nodeId;
  167. disk.Path = drive.Path;
  168. disk.PDiskCategory = TPDiskCategory(PDiskTypeToPDiskType(driveInfo.Type), driveInfo.Kind);
  169. disk.PDiskConfig = driveInfo.PDiskConfig.GetOrElse(TString());
  170. disk.ReadCentric = driveInfo.ReadCentric;
  171. disk.Serial = serial;
  172. disk.SharedWithOs = driveInfo.SharedWithOs;
  173. auto diskId = disk.GetId();
  174. auto [_, inserted] = disks.try_emplace(diskId, std::move(disk));
  175. if (!inserted) {
  176. throw TExError() << "Came across duplicate disk on node: " << TErrorParams::NodeId(diskId.NodeId) << " with path: " << TErrorParams::Path(diskId.Path);
  177. }
  178. }
  179. }
  180. }
  181. return disks;
  182. }
  183. static std::unordered_map<TDiskId, TDiskInfo> GetDisksFromDrivesSerials(const TBlobStorageController::TConfigState& state, const std::set<TBoxId>& relevantBoxes) {
  184. std::unordered_map<TDiskId, TDiskInfo> disks;
  185. state.DrivesSerials.ForEachInRange({}, {}, [&](const auto& serial, const auto& driveInfo) {
  186. if (!relevantBoxes.contains(driveInfo.BoxId)) {
  187. return true;
  188. }
  189. if (driveInfo.LifeStage != NKikimrBlobStorage::TDriveLifeStage::ADDED_BY_DSTOOL) {
  190. return true;
  191. }
  192. if (serial.Serial.empty()) {
  193. throw TExError() << "Missing disks's serial number";
  194. }
  195. auto nodeId = driveInfo.NodeId;
  196. if (!nodeId) {
  197. throw TExError() << "Empty node id for disk with serial number " << TErrorParams::DiskSerialNumber(serial.Serial);
  198. }
  199. auto hostId = state.HostRecords->GetHostId(*nodeId);
  200. if (!hostId) {
  201. throw TExError() << "Couldn't find host id for node " << TErrorParams::NodeId(*nodeId);
  202. }
  203. auto path = driveInfo.Path;
  204. if (!path) {
  205. throw TExError() << "Couldn't get path for disk with serial number " << TErrorParams::DiskSerialNumber(serial.Serial);
  206. }
  207. TDiskInfo disk;
  208. disk.BoxId = driveInfo.BoxId;
  209. disk.HostId = *hostId;
  210. disk.LastSeenPath = GetDiskPathFromNode(*nodeId, serial, state, /* throwOnError */ false);
  211. disk.LastSeenSerial = serial;
  212. disk.NodeId = *nodeId;
  213. disk.Path = *path;
  214. disk.PDiskCategory = TPDiskCategory(PDiskTypeToPDiskType(driveInfo.PDiskType), driveInfo.Kind);
  215. disk.PDiskConfig = driveInfo.PDiskConfig.GetOrElse(TString());
  216. disk.ReadCentric = false;
  217. disk.Serial = serial;
  218. disk.SharedWithOs = false;
  219. auto diskId = disk.GetId();
  220. auto [_, inserted] = disks.try_emplace(diskId, std::move(disk));
  221. if (!inserted) {
  222. throw TExError() << "Came across duplicate disk on node: " << TErrorParams::NodeId(diskId.NodeId) << " with path: " << TErrorParams::Path(diskId.Path);
  223. }
  224. return true;
  225. });
  226. return disks;
  227. }
  228. static std::unordered_map<TDiskId, TDiskInfo> GetDisksFromDrivesSerialsAndHostConfig(
  229. const TBlobStorageController::TConfigState& state, const std::set<TBoxId>& relevantBoxes)
  230. {
  231. auto disksFromDrivesSerials = GetDisksFromDrivesSerials(state, relevantBoxes);
  232. auto disksFromHostConfig = GetDisksFromHostConfig(state, relevantBoxes);
  233. disksFromHostConfig.merge(disksFromDrivesSerials);
  234. return disksFromHostConfig;
  235. }
  236. static Schema::PDisk::Guid::Type GetGuidAndValidateStaticPDisk(
  237. const TPDiskId& pdiskId,
  238. const TDiskInfo& disk,
  239. const TBlobStorageController::TConfigState& state,
  240. ui32& staticSlotUsage)
  241. {
  242. const auto& info = state.StaticPDisks.at(pdiskId);
  243. // create new disk entry; the PDisk with this number MUST NOT exist, otherwise we can
  244. // have a collision
  245. if (state.PDisks.Find(pdiskId)) {
  246. throw TExError() << "PDisk from static config collides with dynamic one"
  247. << TErrorParams::NodeId(pdiskId.NodeId) << TErrorParams::PDiskId(pdiskId.PDiskId);
  248. }
  249. // validate fields
  250. if (disk.PDiskConfig != info.PDiskConfig) {
  251. throw TExError() << "PDiskConfig field doesn't match static one"
  252. << " pdiskConfig# " << (disk.PDiskConfig ? FormatPDiskConfig(disk.PDiskConfig) : "(empty)")
  253. << " info.PDiskConfig# " << FormatPDiskConfig(info.PDiskConfig);
  254. } else if (disk.PDiskCategory != info.Category) {
  255. throw TExError() << "Type/Kind fields do not match static one";
  256. }
  257. staticSlotUsage = info.StaticSlotUsage;
  258. return info.Guid;
  259. }
  260. void TBlobStorageController::FitPDisksForUserConfig(TConfigState& state) {
  261. auto relevantBoxes = std::exchange(state.Fit.Boxes, {});
  262. if (relevantBoxes.empty()) {
  263. return;
  264. }
  265. // re-fill PDisksToRemove set with all PDisks, we will erase remaining ones from this set a bit later
  266. state.PDisksToRemove.clear();
  267. state.PDisks.ForEach([&](const TPDiskId& pdiskId, const TPDiskInfo& pdiskInfo) {
  268. if (relevantBoxes.contains(pdiskInfo.BoxId)) {
  269. state.PDisksToRemove.insert(pdiskId);
  270. }
  271. return true;
  272. });
  273. auto disks = GetDisksFromDrivesSerialsAndHostConfig(state, relevantBoxes);
  274. for (const auto& [diskId, disk] : disks) {
  275. TPDiskId pdiskId;
  276. // check if we already have spawned some PDisk at this location
  277. if (auto pdiskIdOptional = NKikimr::NBsController::FindPDisk(disk, state)) {
  278. // yes, we have; find it by id and update some characteristics (that we can update)
  279. pdiskId = *pdiskIdOptional;
  280. UpdatePDiskIfNeeded(pdiskId, disk, DefaultMaxSlots, state);
  281. } else {
  282. // no, we haven't; see if it is mentioned in static configuration
  283. ui32 staticSlotUsage = 0;
  284. Schema::PDisk::Guid::Type guid{};
  285. if (auto pdiskIdOptional = NKikimr::NBsController::FindStaticPDisk(disk, state)) {
  286. // yes, take some data from static configuration
  287. pdiskId = *pdiskIdOptional;
  288. guid = GetGuidAndValidateStaticPDisk(pdiskId, disk, state, staticSlotUsage);
  289. } else if (auto info = state.DrivesSerials.Find(disk.Serial); info && info->Guid) {
  290. pdiskId = FindFirstEmptyPDiskId(state.PDisks, disk.NodeId);
  291. guid = *info->Guid;
  292. } else {
  293. pdiskId = FindFirstEmptyPDiskId(state.PDisks, disk.NodeId);
  294. guid = RandomNumber<Schema::PDisk::Guid::Type>();
  295. }
  296. // create PDisk
  297. state.PDisks.ConstructInplaceNewEntry(pdiskId, disk.HostId, disk.Path,
  298. disk.PDiskCategory.GetRaw(), guid, disk.SharedWithOs, disk.ReadCentric,
  299. /* nextVslotId */ 1000, disk.PDiskConfig, disk.BoxId, DefaultMaxSlots,
  300. NKikimrBlobStorage::EDriveStatus::ACTIVE, /* statusTimestamp */ TInstant::Zero(),
  301. NKikimrBlobStorage::EDecommitStatus::DECOMMIT_NONE,
  302. disk.Serial, disk.LastSeenSerial, disk.LastSeenPath, staticSlotUsage);
  303. // Set PDiskId and Guid in DrivesSerials
  304. if (auto info = state.DrivesSerials.FindForUpdate(disk.Serial)) {
  305. info->PDiskId = pdiskId.PDiskId;
  306. info->Guid = guid;
  307. }
  308. STLOG(PRI_NOTICE, BS_CONTROLLER, BSCFP02, "Create new pdisk", (PDiskId, pdiskId), (Path, disk.Path));
  309. }
  310. state.PDisksToRemove.erase(pdiskId);
  311. }
  312. for (const auto& pdiskId : state.PDisksToRemove) {
  313. STLOG(PRI_NOTICE, BS_CONTROLLER, BSCFP03, "PDisk to remove:", (PDiskId, pdiskId));
  314. }
  315. state.CheckConsistency();
  316. }
  317. } // NBsController
  318. } // NKikimr