diff --git a/docs/config/osd.en.md b/docs/config/osd.en.md index f5b0739a..093fdbb9 100644 --- a/docs/config/osd.en.md +++ b/docs/config/osd.en.md @@ -45,6 +45,7 @@ them, even without restarting by updating configuration in etcd. - [scrub_queue_depth](#scrub_queue_depth) - [scrub_sleep](#scrub_sleep) - [scrub_list_limit](#scrub_list_limit) +- [scrub_find_best](#scrub_find_best) - [scrub_ec_max_bruteforce](#scrub_ec_max_bruteforce) ## etcd_report_interval @@ -395,6 +396,23 @@ Can be used to slow down scrubbing if it affects user load too much. Number of objects to list in one listing operation during scrub. +## scrub_find_best + +- Type: boolean +- Default: true +- Can be changed online: yes + +Find and automatically restore best versions of objects with unmatched +copies. In replicated setups, the best version is the version with most +matching replicas. In EC setups, the best version is the subset of data +and parity chunks without mismatches. + +The hypothetical situation where you might want to disable it is when +you have 3 replicas and you are paranoid that 2 HDDs out of 3 may silently +corrupt an object in the same way (for example, zero it out) and only +1 HDD will remain good. In this case disabling scrub_find_best may help +you to recover the data! See also scrub_ec_max_bruteforce below. + ## scrub_ec_max_bruteforce - Type: integer diff --git a/docs/config/osd.ru.md b/docs/config/osd.ru.md index 65801e5c..7dd8d430 100644 --- a/docs/config/osd.ru.md +++ b/docs/config/osd.ru.md @@ -46,6 +46,7 @@ - [scrub_queue_depth](#scrub_queue_depth) - [scrub_sleep](#scrub_sleep) - [scrub_list_limit](#scrub_list_limit) +- [scrub_find_best](#scrub_find_best) - [scrub_ec_max_bruteforce](#scrub_ec_max_bruteforce) ## etcd_report_interval @@ -414,6 +415,25 @@ Flusher - это микро-поток (корутина), которая коп Размер загружаемых за одну операцию списков объектов в процессе фоновой проверки. +## scrub_find_best + +- Тип: булево (да/нет) +- Значение по умолчанию: true +- Можно менять на лету: да + +Находить и автоматически восстанавливать "лучшие версии" объектов с +несовпадающими копиями/частями. При использовании репликации "лучшая" +версия - версия, доступная в большем числе экземпляров, чем другие. При +использовании кодов коррекции ошибок "лучшая" весрия - это подмножество +частей данных и чётности, полностью соответствующих друг другу. + +Гипотетическая ситуация, в которой вы можете захотеть отключить этот +поиск - это если у вас 3 реплики и вы боитесь, что 2 диска из 3 могут +незаметно и одинаково повредить данные одного и того же объекта, например, +занулив его, и только 1 диск останется неповреждённым. В этой ситуации +отключение этого параметра поможет вам восстановить данные! Смотрите также +описание следующего параметра - scrub_ec_max_bruteforce. + ## scrub_ec_max_bruteforce - Тип: целое число diff --git a/docs/config/src/osd.yml b/docs/config/src/osd.yml index cdd2af68..e93ec025 100644 --- a/docs/config/src/osd.yml +++ b/docs/config/src/osd.yml @@ -444,6 +444,34 @@ info_ru: | Размер загружаемых за одну операцию списков объектов в процессе фоновой проверки. +- name: scrub_find_best + type: bool + default: true + online: true + info: | + Find and automatically restore best versions of objects with unmatched + copies. In replicated setups, the best version is the version with most + matching replicas. In EC setups, the best version is the subset of data + and parity chunks without mismatches. + + The hypothetical situation where you might want to disable it is when + you have 3 replicas and you are paranoid that 2 HDDs out of 3 may silently + corrupt an object in the same way (for example, zero it out) and only + 1 HDD will remain good. In this case disabling scrub_find_best may help + you to recover the data! See also scrub_ec_max_bruteforce below. + info_ru: | + Находить и автоматически восстанавливать "лучшие версии" объектов с + несовпадающими копиями/частями. При использовании репликации "лучшая" + версия - версия, доступная в большем числе экземпляров, чем другие. При + использовании кодов коррекции ошибок "лучшая" весрия - это подмножество + частей данных и чётности, полностью соответствующих друг другу. + + Гипотетическая ситуация, в которой вы можете захотеть отключить этот + поиск - это если у вас 3 реплики и вы боитесь, что 2 диска из 3 могут + незаметно и одинаково повредить данные одного и того же объекта, например, + занулив его, и только 1 диск останется неповреждённым. В этой ситуации + отключение этого параметра поможет вам восстановить данные! Смотрите также + описание следующего параметра - scrub_ec_max_bruteforce. - name: scrub_ec_max_bruteforce type: int default: 100 diff --git a/mon/mon.js b/mon/mon.js index ac2e7c70..7041b23a 100644 --- a/mon/mon.js +++ b/mon/mon.js @@ -117,6 +117,7 @@ const etcd_tree = { scrub_queue_depth: 1, scrub_sleep: 0, // milliseconds scrub_list_limit: 1000, // objects to list on one scrub iteration + scrub_find_best: true, scrub_ec_max_bruteforce: 100, // maximum EC error locator brute-force iterators // blockstore - fixed in superblock block_size, diff --git a/src/osd.cpp b/src/osd.cpp index 54e69941..32157ab1 100644 --- a/src/osd.cpp +++ b/src/osd.cpp @@ -218,6 +218,7 @@ void osd_t::parse_config(bool init) scrub_queue_depth = config["scrub_queue_depth"].uint64_value(); if (scrub_queue_depth < 1 || scrub_queue_depth > MAX_RECOVERY_QUEUE) scrub_queue_depth = 1; + scrub_find_best = !json_is_false(config["scrub_find_best"]); scrub_ec_max_bruteforce = config["scrub_ec_max_bruteforce"].uint64_value(); if (scrub_ec_max_bruteforce < 1) scrub_ec_max_bruteforce = 100; diff --git a/src/osd.h b/src/osd.h index 5f22cf71..7583b5be 100644 --- a/src/osd.h +++ b/src/osd.h @@ -120,6 +120,7 @@ class osd_t uint64_t scrub_queue_depth = 1; uint64_t scrub_sleep_ms = 0; uint32_t scrub_list_limit = 1000; + bool scrub_find_best = true; uint64_t scrub_ec_max_bruteforce = 100; // cluster state diff --git a/src/osd_scrub.cpp b/src/osd_scrub.cpp index ac99feba..772cc9b6 100644 --- a/src/osd_scrub.cpp +++ b/src/osd_scrub.cpp @@ -507,18 +507,28 @@ resume_2: for (int role = 0; role < op_data->pg_size; role++) { if (role != best && votes[role] == votes[best]) + { unknown = true; + } if (votes[role] > 0 && votes[role] < votes[best]) { printf( - "[PG %u/%u] Object %lx:%lx v%lu copy on OSD %lu doesn't match %d other copies, marking it as corrupted\n", + "[PG %u/%u] Object %lx:%lx v%lu copy on OSD %lu doesn't match %d other copies%s\n", INODE_POOL(op_data->oid.inode), op_data->pg_num, op_data->oid.inode, op_data->oid.stripe, op_data->fact_ver, - op_data->stripes[role].osd_num, votes[best] + op_data->stripes[role].osd_num, votes[best], + scrub_find_best ? ", marking it as corrupted" : "" ); - op_data->stripes[role].read_error = true; + if (scrub_find_best) + { + op_data->stripes[role].read_error = true; + } } } + if (!scrub_find_best) + { + unknown = true; + } if (unknown) { // It's unknown which replica is good. There are multiple versions with no majority @@ -551,10 +561,14 @@ resume_2: } else { + int total = 0; for (int role = 0; role < op_data->pg_size; role++) { if (!op_data->stripes[role].missing) + { + total++; op_data->stripes[role].read_error = true; + } } for (int role: good_subset) { @@ -564,15 +578,32 @@ resume_2: { if (!op_data->stripes[role].missing && op_data->stripes[role].read_error) { - op_data->stripes[role].read_error = true; printf( - "[PG %u/%u] Object %lx:%lx v%lu chunk %d on OSD %lu doesn't match data, marking it as corrupted\n", + "[PG %u/%u] Object %lx:%lx v%lu chunk %d on OSD %lu doesn't match other chunks%s\n", INODE_POOL(op_data->oid.inode), op_data->pg_num, op_data->oid.inode, op_data->oid.stripe, op_data->fact_ver, - role, op_data->stripes[role].osd_num + role, op_data->stripes[role].osd_num, + scrub_find_best ? ", marking it as corrupted" : "" ); } } + if (!scrub_find_best && good_subset.size() < total) + { + inconsistent = true; + printf( + "[PG %u/%u] Object %lx:%lx v%lu is marked as inconsistent because scrub_find_best is turned off. Use vitastor-cli fix to fix it\n", + INODE_POOL(op_data->oid.inode), op_data->pg_num, + op_data->oid.inode, op_data->oid.stripe, op_data->fact_ver + ); + for (int role = 0; role < op_data->pg_size; role++) + { + if (!op_data->stripes[role].missing && op_data->stripes[role].read_error) + { + // Undo error locator marking chunk as bad + op_data->stripes[role].read_error = false; + } + } + } } } for (int role = 0; role < op_data->pg_size; role++)