if (scrub_after_recovery) {
dout(10) << "_finish_recovery requeueing for scrub" << dendl;
scrub_after_recovery = false;
+ scrubber.must_deep_scrub = true;
queue_scrub();
}
} else {
}
}
-bool PG::_compare_scrub_objects(ScrubMap::object &auth,
+enum PG::error_type PG::_compare_scrub_objects(ScrubMap::object &auth,
ScrubMap::object &candidate,
ostream &errorstream)
{
- bool ok = true;
+ enum PG::error_type error = CLEAN;
if (candidate.read_error) {
- ok = false;
+ // This can occur on stat() of a shallow scrub, but in that case size will
+ // be invalid, and this will be over-ridden below.
+ error = DEEP_ERROR;
errorstream << "candidate had a read error";
}
- if (auth.size != candidate.size) {
- ok = false;
- errorstream << "size " << candidate.size
- << " != known size " << auth.size;
- }
if (auth.digest_present && candidate.digest_present) {
if (auth.digest != candidate.digest) {
- if (!ok)
+ if (error != CLEAN)
errorstream << ", ";
- ok = false;
+ error = DEEP_ERROR;
errorstream << "digest " << candidate.digest
<< " != known digest " << auth.digest;
}
if (auth.omap_digest_present && candidate.omap_digest_present) {
if (auth.omap_digest != candidate.omap_digest) {
- if (!ok)
+ if (error != CLEAN)
errorstream << ", ";
- ok = false;
+ error = DEEP_ERROR;
errorstream << "omap_digest " << candidate.omap_digest
<< " != known omap_digest " << auth.omap_digest;
}
}
+ // Shallow error takes precendence because this will be seen by
+ // both types of scrubs.
+ if (auth.size != candidate.size) {
+ if (error != CLEAN)
+ errorstream << ", ";
+ error = SHALLOW_ERROR;
+ errorstream << "size " << candidate.size
+ << " != known size " << auth.size;
+ }
for (map<string,bufferptr>::const_iterator i = auth.attrs.begin();
i != auth.attrs.end();
++i) {
if (!candidate.attrs.count(i->first)) {
- if (!ok)
- errorstream << ", ";
- ok = false;
+ if (error != CLEAN)
+ errorstream << ", ";
+ error = SHALLOW_ERROR;
errorstream << "missing attr " << i->first;
} else if (candidate.attrs.find(i->first)->second.cmp(i->second)) {
- if (!ok)
- errorstream << ", ";
- ok = false;
+ if (error != CLEAN)
+ errorstream << ", ";
+ error = SHALLOW_ERROR;
errorstream << "attr value mismatch " << i->first;
}
}
i != candidate.attrs.end();
++i) {
if (!auth.attrs.count(i->first)) {
- if (!ok)
- errorstream << ", ";
- ok = false;
+ if (error != CLEAN)
+ errorstream << ", ";
+ error = SHALLOW_ERROR;
errorstream << "extra attr " << i->first;
}
}
- return ok;
+ return error;
}
if (j->second->objects.count(*k)) {
// Compare
stringstream ss;
- if (!_compare_scrub_objects(auth->second->objects[*k],
+ enum PG::error_type error = _compare_scrub_objects(auth->second->objects[*k],
j->second->objects[*k],
- ss)) {
+ ss);
+ if (error != CLEAN) {
cur_inconsistent.insert(j->first);
- ++scrubber.errors;
+ if (error == SHALLOW_ERROR)
+ ++scrubber.shallow_errors;
+ else
+ ++scrubber.deep_errors;
errorstream << info.pgid << " osd." << acting[j->first]
<< ": soid " << *k << " " << ss.str() << std::endl;
}
} else {
cur_missing.insert(j->first);
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
errorstream << info.pgid
<< " osd." << acting[j->first]
<< " missing " << *k << std::endl;
for (set<int>::iterator j = obj->second.begin();
j != obj->second.end();
++j) {
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
ss << info.pgid << " " << mode << " " << " object " << obj->first
<< " has inconsistent snapcolls on " << *j << std::endl;
}
{
stringstream oss;
oss << info.pgid << " " << mode << " ";
- if (scrubber.errors)
- oss << scrubber.errors << " errors";
+ int total_errors = scrubber.shallow_errors + scrubber.deep_errors;
+ if (total_errors)
+ oss << total_errors << " errors";
else
oss << "ok";
+ if (!deep_scrub && info.stats.stats.sum.num_deep_scrub_errors)
+ oss << " ( " << info.stats.stats.sum.num_deep_scrub_errors
+ << " remaining deep scrub error(s) )";
if (repair)
oss << ", " << scrubber.fixed << " fixed";
oss << "\n";
- if (scrubber.errors)
+ if (total_errors)
osd->clog.error(oss);
else
osd->clog.info(oss);
info.history.last_deep_scrub = info.last_update;
info.history.last_deep_scrub_stamp = now;
}
- if (scrubber.errors == 0)
- info.history.last_clean_scrub_stamp = now;
- info.stats.stats.sum.num_scrub_errors = scrubber.errors;
+ if (deep_scrub) {
+ if ((scrubber.shallow_errors == 0) && (scrubber.deep_errors == 0))
+ info.history.last_clean_scrub_stamp = now;
+ info.stats.stats.sum.num_shallow_scrub_errors = scrubber.shallow_errors;
+ info.stats.stats.sum.num_deep_scrub_errors = scrubber.deep_errors;
+ } else {
+ info.stats.stats.sum.num_shallow_scrub_errors = scrubber.shallow_errors;
+ // XXX: last_clean_scrub_stamp doesn't mean the pg is not inconsistent
+ // because of deep-scrub errors
+ if (scrubber.shallow_errors == 0)
+ info.history.last_clean_scrub_stamp = now;
+ }
+ info.stats.stats.sum.num_scrub_errors =
+ info.stats.stats.sum.num_shallow_scrub_errors +
+ info.stats.stats.sum.num_deep_scrub_errors;
reg_next_scrub();
{
reserved(false), reserve_failed(false),
epoch_start(0),
block_writes(false), active(false), queue_snap_trim(false),
- waiting_on(0), errors(0), fixed(0), active_rep_scrub(0),
+ waiting_on(0), shallow_errors(0), deep_errors(0), fixed(0),
+ active_rep_scrub(0),
must_scrub(false), must_deep_scrub(false), must_repair(false),
classic(false),
finalizing(false), is_chunky(false), state(INACTIVE),
bool queue_snap_trim;
int waiting_on;
set<int> waiting_on_whom;
- int errors;
+ int shallow_errors;
+ int deep_errors;
int fixed;
ScrubMap primary_scrubmap;
map<int,ScrubMap> received_maps;
// flags to indicate explicitly requested scrubs (by admin)
bool must_scrub, must_deep_scrub, must_repair;
- // Maps from objects with erros to missing/inconsistent peers
+ // Maps from objects with errors to missing/inconsistent peers
map<hobject_t, set<int> > missing;
map<hobject_t, set<int> > inconsistent;
map<hobject_t, set<int> > inconsistent_snapcolls;
start = hobject_t();
end = hobject_t();
subset_last_update = eversion_t();
- errors = 0;
+ shallow_errors = 0;
+ deep_errors = 0;
fixed = 0;
deep = false;
run_callbacks();
map<int, ScrubMap *>::const_iterator _select_auth_object(
const hobject_t &obj,
const map<int,ScrubMap*> &maps);
- bool _compare_scrub_objects(ScrubMap::object &auth,
+
+ enum error_type {
+ CLEAN,
+ DEEP_ERROR,
+ SHALLOW_ERROR
+ };
+ enum error_type _compare_scrub_objects(ScrubMap::object &auth,
ScrubMap::object &candidate,
ostream &errorstream);
void _compare_scrubmaps(const map<int,ScrubMap*> &maps,
if (p->second.attrs.count(SS_ATTR) == 0) {
osd->clog.error() << mode << " " << info.pgid << " " << soid
<< " no '" << SS_ATTR << "' attr";
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
continue;
}
bufferlist bl;
if (head != hobject_t()) {
osd->clog.error() << mode << " " << info.pgid << " " << head
<< " missing clones";
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
}
// what will be next?
if (p->second.attrs.count(OI_ATTR) == 0) {
osd->clog.error() << mode << " " << info.pgid << " " << soid
<< " no '" << OI_ATTR << "' attr";
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
continue;
}
bufferlist bv;
osd->clog.error() << mode << " " << info.pgid << " " << soid
<< " on disk size (" << p->second.size
<< ") does not match object info size (" << oi.size << ")";
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
}
dout(20) << mode << " " << soid << " " << oi << dendl;
if (!snapset.head_exists) {
osd->clog.error() << mode << " " << info.pgid << " " << soid
<< " snapset.head_exists=false, but object exists";
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
continue;
}
} else if (soid.snap) {
if (head == hobject_t()) {
osd->clog.error() << mode << " " << info.pgid << " " << soid
<< " found clone without head";
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
continue;
}
if (soid.snap != *curclone) {
osd->clog.error() << mode << " " << info.pgid << " " << soid
<< " expected clone " << *curclone;
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
assert(soid.snap == *curclone);
}
<< scrub_cstat.sum.num_objects << "/" << info.stats.stats.sum.num_objects << " objects, "
<< scrub_cstat.sum.num_object_clones << "/" << info.stats.stats.sum.num_object_clones << " clones, "
<< scrub_cstat.sum.num_bytes << "/" << info.stats.stats.sum.num_bytes << " bytes.\n";
- ++scrubber.errors;
+ ++scrubber.shallow_errors;
if (repair) {
++scrubber.fixed;
f->dump_int("num_write", num_wr);
f->dump_int("num_write_kb", num_wr_kb);
f->dump_int("num_scrub_errors", num_scrub_errors);
+ f->dump_int("num_shallow_scrub_errors", num_shallow_scrub_errors);
+ f->dump_int("num_deep_scrub_errors", num_deep_scrub_errors);
f->dump_int("num_objects_recovered", num_objects_recovered);
f->dump_int("num_bytes_recovered", num_bytes_recovered);
f->dump_int("num_keys_recovered", num_keys_recovered);
void object_stat_sum_t::encode(bufferlist& bl) const
{
- ENCODE_START(5, 3, bl);
+ ENCODE_START(6, 3, bl);
::encode(num_bytes, bl);
::encode(num_objects, bl);
::encode(num_object_clones, bl);
::encode(num_objects_recovered, bl);
::encode(num_bytes_recovered, bl);
::encode(num_keys_recovered, bl);
+ ::encode(num_shallow_scrub_errors, bl);
+ ::encode(num_deep_scrub_errors, bl);
ENCODE_FINISH(bl);
}
void object_stat_sum_t::decode(bufferlist::iterator& bl)
{
- DECODE_START_LEGACY_COMPAT_LEN(5, 3, 3, bl);
+ DECODE_START_LEGACY_COMPAT_LEN(6, 3, 3, bl);
::decode(num_bytes, bl);
if (struct_v < 3) {
uint64_t num_kb;
num_bytes_recovered = 0;
num_keys_recovered = 0;
}
+ if (struct_v >= 6) {
+ ::decode(num_shallow_scrub_errors, bl);
+ ::decode(num_deep_scrub_errors, bl);
+ } else {
+ num_shallow_scrub_errors = 0;
+ num_deep_scrub_errors = 0;
+ }
DECODE_FINISH(bl);
}
a.num_objects_unfound = 8;
a.num_rd = 9; a.num_rd_kb = 10;
a.num_wr = 11; a.num_wr_kb = 12;
- a.num_scrub_errors = 13;
a.num_objects_recovered = 14;
a.num_bytes_recovered = 15;
a.num_keys_recovered = 16;
+ a.num_deep_scrub_errors = 17;
+ a.num_shallow_scrub_errors = 18;
+ a.num_scrub_errors = a.num_deep_scrub_errors + a.num_shallow_scrub_errors;
o.push_back(new object_stat_sum_t(a));
}
num_wr_kb += o.num_wr_kb;
num_objects_unfound += o.num_objects_unfound;
num_scrub_errors += o.num_scrub_errors;
+ num_shallow_scrub_errors += o.num_shallow_scrub_errors;
+ num_deep_scrub_errors += o.num_deep_scrub_errors;
num_objects_recovered += o.num_objects_recovered;
num_bytes_recovered += o.num_bytes_recovered;
num_keys_recovered += o.num_keys_recovered;
num_wr_kb -= o.num_wr_kb;
num_objects_unfound -= o.num_objects_unfound;
num_scrub_errors -= o.num_scrub_errors;
+ num_shallow_scrub_errors -= o.num_shallow_scrub_errors;
+ num_deep_scrub_errors -= o.num_deep_scrub_errors;
num_objects_recovered -= o.num_objects_recovered;
num_bytes_recovered -= o.num_bytes_recovered;
num_keys_recovered -= o.num_keys_recovered;
int64_t num_objects_unfound;
int64_t num_rd, num_rd_kb;
int64_t num_wr, num_wr_kb;
- int64_t num_scrub_errors;
+ int64_t num_scrub_errors; // total deep and shallow scrub errors
+ int64_t num_shallow_scrub_errors;
+ int64_t num_deep_scrub_errors;
int64_t num_objects_recovered;
int64_t num_bytes_recovered;
int64_t num_keys_recovered;
num_objects(0), num_object_clones(0), num_object_copies(0),
num_objects_missing_on_primary(0), num_objects_degraded(0), num_objects_unfound(0),
num_rd(0), num_rd_kb(0), num_wr(0), num_wr_kb(0),
- num_scrub_errors(0),
+ num_scrub_errors(0), num_shallow_scrub_errors(0),
+ num_deep_scrub_errors(0),
num_objects_recovered(0),
num_bytes_recovered(0),
num_keys_recovered(0)
bool read_error;
object() :
- size(0), negative(false), digest(0), digest_present(false),
+ // Init invalid size so it won't match if we get a stat EIO error
+ size(-1), negative(false), digest(0), digest_present(false),
nlinks(0), omap_digest(0), omap_digest_present(false),
read_error(false) {}