DAOS-19616 placement: write relocated shard to both targets - #19086
Open
liuxuezhao wants to merge 1 commit into
Open
liuxuezhao wants to merge 1 commit into
liuxuezhao wants to merge 1 commit into
Conversation
A shard which an earlier rebuild put on a spare can be handed a
different spare by a new, unrelated failure: obj_remap_shards() walks
one shared spare sequence for the whole object and requeues a shard
whose candidate is unavailable, so everybody behind it shifts. The
CURRENT layout then names a target which was never written to, while
the data is on the old one, which the write path stops using. The
PRE_REBUILD layout, used by the migration fetch and every read only
client, keeps reading the old target and goes stale, and a read-write
client is offered the empty target unless the remap happened to flag
it (determine_valid_spares() only ORs in the flags of the rejected
candidates, so the rebuilding flag is set arbitrarily).
Write the relocated shard to both targets, the way a drain is
handled, instead of hiding it from the readers:
layout_keep_relocated_source() compares the CURRENT layout with the
PRE_REBUILD one, puts the old, still serving target back as the
readable primary, and appends the new target as a write-only peer
through pl_map_extend(). Every write then lands on both the old and
the new target while the migration copies the old one to the new one,
so neither reader nor migration ever sees an empty target. When
a drain, reintegration or extension is in flight as well, the layout
is extended from the diff against POST_REBUILD anyway, which names the
target the migration really goes to, so only the primary is put back
and the diff supplies the peer. A shard whose old target is down is
flagged rebuilding when the remap missed it, and a spurious rebuilding
flag on an unchanged spare is dropped.
The read only path computes nothing extra. The read-write path
computes one extra PRE_REBUILD layout only when the placement asked
about a DOWN, DRAIN or UP target (mode_dependent); otherwise the two
modes provably agree. No further "on spare" gate is applied: during
extension, NEW domains count towards the object class limit but are
excluded from the initial placement, so a group wider than the old
domain set reuses a domain and a fresh failure can change the initial
target selection through reset_dom_cur_grp_v1() without any shard
sitting on a historical spare. Such a gate dropped a healthy
PRE_REBUILD read source from the write set (RP_5G1, layout v1).
Also included: pl_map_extend() propagates the peer allocation failure
instead of overwriting it with 0; the redundant min() of two layout
sizes asserted equal is removed; and the migration comment on a fetch
returning iod_size 0 now describes the real causes (placement
mismatch, older fetch epoch, unresolved DTX) and why -DER_DATA_LOSS is
excluded from the rebuild status.
Placement UT additions: no_stale_read_source, no_hidden_peer_source,
extension_preserves_read_sources (RP_5G1/RP_5G2/EC_4P1G1/EC_4P1G2,
one/two groups, layout v1/v2, NEW and new-UP extension) and
relocation_peer_alloc_failure (fault injection). They assert that the
PRE_REBUILD read sources stay in the CURRENT write set, that every
readable CURRENT entry was written to, that no target is readable
twice in a group, and that every migration destination is written to.
To build and run the reproducer from the repository root:
scons --config=force --jobs=16 \
install/bin/jump_pl_map install/lib64/libdaos.so \
install/lib64/libdaos_common.so install/lib64/libgurt.so
./utils/run_utest.py --suite_filter='^placement$' --sudo=no
With CMocka filtering enabled, a single case can be selected, e.g.
install/bin/jump_pl_map -f extension_preserves_read_sources; without
it the binary runs the whole placement group. With the tests but
without the fix, no_stale_read_source, no_hidden_peer_source and
extension_preserves_read_sources fail on the first check (a
PRE_REBUILD read source the CURRENT layout does not write to); with
the fix all 43 placement cases pass. No cluster or VOS I/O is needed.
Signed-off-by: Xuezhao Liu <xuezhao.liu@hpe.com>
|
Ticket title is 'FIO crc mismatch detected on 4x node IB config while running HSN cable pulls test' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A shard which an earlier rebuild put on a spare can be handed a different spare by a new, unrelated failure: obj_remap_shards() walks one shared spare sequence for the whole object and requeues a shard whose candidate is unavailable, so everybody behind it shifts. The CURRENT layout then names a target which was never written to, while the data is on the old one, which the write path stops using. The PRE_REBUILD layout, used by the migration fetch and every read only client, keeps reading the old target and goes stale, and a read-write client is offered the empty target unless the remap happened to flag it (determine_valid_spares() only ORs in the flags of the rejected candidates, so the rebuilding flag is set arbitrarily).
Write the relocated shard to both targets, the way a drain is handled, instead of hiding it from the readers:
layout_keep_relocated_source() compares the CURRENT layout with the PRE_REBUILD one, puts the old, still serving target back as the readable primary, and appends the new target as a write-only peer through pl_map_extend(). Every write then lands on both the old and the new target while the migration copies the old one to the new one, so neither reader nor migration ever sees an empty target. When a drain, reintegration or extension is in flight as well, the layout is extended from the diff against POST_REBUILD anyway, which names the target the migration really goes to, so only the primary is put back and the diff supplies the peer. A shard whose old target is down is flagged rebuilding when the remap missed it, and a spurious rebuilding flag on an unchanged spare is dropped.
The read only path computes nothing extra. The read-write path computes one extra PRE_REBUILD layout only when the placement asked about a DOWN, DRAIN or UP target (mode_dependent); otherwise the two modes provably agree. No further "on spare" gate is applied: during extension, NEW domains count towards the object class limit but are excluded from the initial placement, so a group wider than the old domain set reuses a domain and a fresh failure can change the initial target selection through reset_dom_cur_grp_v1() without any shard sitting on a historical spare. Such a gate dropped a healthy PRE_REBUILD read source from the write set (RP_5G1, layout v1).
Also included: pl_map_extend() propagates the peer allocation failure instead of overwriting it with 0; the redundant min() of two layout sizes asserted equal is removed; and the migration comment on a fetch returning iod_size 0 now describes the real causes (placement mismatch, older fetch epoch, unresolved DTX) and why -DER_DATA_LOSS is excluded from the rebuild status.
Placement UT additions: no_stale_read_source, no_hidden_peer_source, extension_preserves_read_sources (RP_5G1/RP_5G2/EC_4P1G1/EC_4P1G2, one/two groups, layout v1/v2, NEW and new-UP extension) and relocation_peer_alloc_failure (fault injection). They assert that the PRE_REBUILD read sources stay in the CURRENT write set, that every readable CURRENT entry was written to, that no target is readable twice in a group, and that every migration destination is written to.
To build and run the reproducer from the repository root:
scons --config=force --jobs=16
install/bin/jump_pl_map install/lib64/libdaos.so
install/lib64/libdaos_common.so install/lib64/libgurt.so
./utils/run_utest.py --suite_filter='^placement$' --sudo=no
With CMocka filtering enabled, a single case can be selected, e.g. install/bin/jump_pl_map -f extension_preserves_read_sources; without it the binary runs the whole placement group. With the tests but without the fix, no_stale_read_source, no_hidden_peer_source and extension_preserves_read_sources fail on the first check (a PRE_REBUILD read source the CURRENT layout does not write to); with the fix all 43 placement cases pass. No cluster or VOS I/O is needed.
Steps for the author:
After all prior steps are complete: