Fix EC PGs hanging in REPEERING when flush error is last in the batch
This commit is contained in:
+13
-6
@@ -79,9 +79,9 @@ void osd_t::handle_flush_op(bool rollback, pool_id_t pool_id, pg_num_t pg_num, p
|
|||||||
// Throw the result away
|
// Throw the result away
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fb->flush_done++;
|
|
||||||
if (retval != 0)
|
if (retval != 0)
|
||||||
{
|
{
|
||||||
|
fb->flush_errors++;
|
||||||
if (peer_osd == this->osd_num)
|
if (peer_osd == this->osd_num)
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
@@ -97,17 +97,26 @@ void osd_t::handle_flush_op(bool rollback, pool_id_t pool_id, pg_num_t pg_num, p
|
|||||||
auto peer_it = msgr.osd_peers.find(peer_osd);
|
auto peer_it = msgr.osd_peers.find(peer_osd);
|
||||||
if (peer_it != msgr.osd_peers.end())
|
if (peer_it != msgr.osd_peers.end())
|
||||||
{
|
{
|
||||||
// Will repeer/stop this PG
|
// stop_client won't repeer the PG because flush_batch is not deleted yet
|
||||||
msgr.stop_client(peer_it->second->client_id);
|
msgr.stop_client(peer_it->second->client_id);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto errors = fb->flush_errors;
|
||||||
|
fb->flush_done++;
|
||||||
if (fb->flush_done == fb->flush_ops)
|
if (fb->flush_done == fb->flush_ops)
|
||||||
{
|
{
|
||||||
// This flush batch is done
|
// This flush batch is done
|
||||||
std::vector<osd_op_t*> continue_ops;
|
|
||||||
auto & pg = pgs.at(pg_id);
|
auto & pg = pgs.at(pg_id);
|
||||||
|
delete fb;
|
||||||
|
pg.flush_batch = NULL;
|
||||||
|
if (errors > 0)
|
||||||
|
{
|
||||||
|
// Repeer the PG on errors
|
||||||
|
repeer_pg(pg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::vector<osd_op_t*> continue_ops;
|
||||||
auto it = pg.flush_actions.begin(), prev_it = it;
|
auto it = pg.flush_actions.begin(), prev_it = it;
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
@@ -143,8 +152,6 @@ void osd_t::handle_flush_op(bool rollback, pool_id_t pool_id, pg_num_t pg_num, p
|
|||||||
}
|
}
|
||||||
prev_it = it++;
|
prev_it = it++;
|
||||||
}
|
}
|
||||||
delete fb;
|
|
||||||
pg.flush_batch = NULL;
|
|
||||||
if (!pg.flush_actions.size())
|
if (!pg.flush_actions.size())
|
||||||
{
|
{
|
||||||
pg.state = pg.state & ~PG_HAS_UNCLEAN;
|
pg.state = pg.state & ~PG_HAS_UNCLEAN;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ struct pg_flush_batch_t
|
|||||||
{
|
{
|
||||||
std::map<osd_num_t, std::vector<obj_ver_id>> rollback_lists;
|
std::map<osd_num_t, std::vector<obj_ver_id>> rollback_lists;
|
||||||
std::map<osd_num_t, std::vector<obj_ver_id>> stable_lists;
|
std::map<osd_num_t, std::vector<obj_ver_id>> stable_lists;
|
||||||
int flush_ops = 0, flush_done = 0;
|
int flush_ops = 0, flush_done = 0, flush_errors = 0;
|
||||||
int flush_objects = 0;
|
int flush_objects = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1138,6 +1138,77 @@ void test_chained_read_eio_retry()
|
|||||||
printf("test_chained_read_eio_retry passed\n");
|
printf("test_chained_read_eio_retry passed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for the bug in osd_flush.cpp where a remote
|
||||||
|
// stabilize/rollback failure permanently hangs the PG in PG_REPEERING.
|
||||||
|
bool test_flush_error_pg_repeer(int ctr)
|
||||||
|
{
|
||||||
|
printf("test_flush_error_pg_repeer\n");
|
||||||
|
|
||||||
|
osd_test_fixture_t f;
|
||||||
|
f.configure_ec_pool(2, 1);
|
||||||
|
f.start(json11::Json::object {
|
||||||
|
{ "osd_num", 2 },
|
||||||
|
{ "etcd_address", "127.0.0.1:2379" },
|
||||||
|
{ "immediate_commit", "all" },
|
||||||
|
{ "block_size", 131072 },
|
||||||
|
{ "bitmap_granularity", 4096 },
|
||||||
|
});
|
||||||
|
f.connect_peer(1);
|
||||||
|
f.connect_peer(3);
|
||||||
|
|
||||||
|
// Object exists on all osds but unstable on osd 1 and 3
|
||||||
|
inode_t ino = INODE_WITH_POOL(1, 1);
|
||||||
|
f.reply_peer_list(1, { { { ino, 0 }, 1 } }, /*stable_count*/ 0);
|
||||||
|
f.reply_local_list({ { { ino, 1 }, 1 } }, /*stable_count*/ 1);
|
||||||
|
f.reply_peer_list(3, { { { ino, 2 }, 1 } }, /*stable_count*/ 0);
|
||||||
|
f.pump();
|
||||||
|
|
||||||
|
auto & pg = f.pg(1, 1);
|
||||||
|
assert(pg.state == (PG_ACTIVE|PG_HAS_UNCLEAN));
|
||||||
|
auto fb = pg.flush_batch;
|
||||||
|
assert(fb != nullptr);
|
||||||
|
assert(fb->flush_ops == 2);
|
||||||
|
|
||||||
|
if (ctr == 0)
|
||||||
|
{
|
||||||
|
// Complete OP_SEC_STAB on osd 1, then fail on osd 3
|
||||||
|
auto *stab = f.peer_take(1, OSD_OP_SEC_STABILIZE);
|
||||||
|
stab->reply.hdr.retval = 0;
|
||||||
|
stab->callback(stab);
|
||||||
|
|
||||||
|
assert(pg.state == (PG_ACTIVE|PG_HAS_UNCLEAN));
|
||||||
|
|
||||||
|
stab = f.peer_take(3, OSD_OP_SEC_STABILIZE);
|
||||||
|
stab->reply.hdr.retval = -EPIPE;
|
||||||
|
stab->callback(stab);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fail OP_SEC_STAB on osd 3, then complete on osd 1
|
||||||
|
auto *stab = f.peer_take(3, OSD_OP_SEC_STABILIZE);
|
||||||
|
stab->reply.hdr.retval = -EPIPE;
|
||||||
|
stab->callback(stab);
|
||||||
|
|
||||||
|
// The PG should only repeer when the batch is completed
|
||||||
|
assert(pg.state == (PG_HAS_UNCLEAN|PG_REPEERING));
|
||||||
|
assert(pg.flush_batch == fb);
|
||||||
|
|
||||||
|
stab = f.peer_take(1, OSD_OP_SEC_STABILIZE);
|
||||||
|
stab->reply.hdr.retval = 0;
|
||||||
|
stab->callback(stab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now the PG should be peering
|
||||||
|
assert(pg.state == PG_PEERING);
|
||||||
|
assert(!pg.flush_batch);
|
||||||
|
|
||||||
|
// FIXME: Make this test pass without leaks without the following line:
|
||||||
|
f.complete_peering_empty();
|
||||||
|
|
||||||
|
printf("test_flush_error_pg_repeer[%d] passed\n", ctr);
|
||||||
|
return ctr < 1;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int narg, char *args[])
|
int main(int narg, char *args[])
|
||||||
{
|
{
|
||||||
test_load_global_config();
|
test_load_global_config();
|
||||||
@@ -1149,5 +1220,6 @@ int main(int narg, char *args[])
|
|||||||
test_ec42_write_parityless();
|
test_ec42_write_parityless();
|
||||||
test_ec33_chain_read_phantom_bitmap_source();
|
test_ec33_chain_read_phantom_bitmap_source();
|
||||||
test_chained_read_eio_retry();
|
test_chained_read_eio_retry();
|
||||||
|
for (int i = 0; test_flush_error_pg_repeer(i); i++) {}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user