-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Backup requests for replicated storage #7
Open
ericox
wants to merge
25
commits into
westerndigitalcorporation:master
Choose a base branch
from
ericox:backup-requests-replicated
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
04bf5c2
request id
a4a6d00
plumbing for reqID in client
ericox 0349476
fix client compile bugs
ericox f1413ea
add cutSlice helper and plumb otherHosts through rpc
ericox d46fb6e
disable backups
ericox 4bbeae9
configurable client backup reads
ericox c6096ed
add read check
ericox 974bc72
rm print
ericox 288ae14
cleanup backup read logic
ericox 3243231
merge two read funcs
ericox 86b5d4c
review feedback
ericox a07e2f7
comments
ericox bf10f9d
move bad ts report logic
ericox 00de983
cleanup orderCh
ericox 4d78d65
cleanup and remove error reporting for now
ericox 4c8ca12
rm reporting
ericox 3adde22
backup error logic
ericox dfa1952
pull logic from fallback loop into helper
ericox 2af38ac
review feedback
ericox ab39a54
review feedback
ericox 84540ed
move select into helper
ericox 571ed3c
refactor again
ericox 7ae0645
backup test scheme + refactor
ericox edfc8db
adding tests
ericox fce1cca
cleanup and tests
ericox File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ import ( | |
"strings" | ||
"sync" | ||
"testing" | ||
"time" | ||
|
||
"github.com/westerndigitalcorporation/blb/pkg/slices" | ||
|
||
|
@@ -65,9 +66,37 @@ func (log *tsTraceLog) check(t *testing.T, write bool, addr string, version, len | |
t.Errorf("trace log missing entry %v %v %v %v %v", write, addr, version, length, off) | ||
} | ||
|
||
// checkRead checks that the specified entry is present and verifies the read host address. | ||
// The tract id itself is embedded in addr (along with the index of the | ||
// tractserver), so we don't bother checking id. | ||
func (log *tsTraceLog) checkRead(t *testing.T, write bool, addr string, version, length int, off int64) { | ||
for _, e := range log.log { | ||
// Reads randomly pick a host to read from so we don't verify the address. | ||
if e.write == write && | ||
e.addr == addr && | ||
e.version == version && | ||
e.length == length && | ||
e.off == off { | ||
return | ||
} | ||
} | ||
t.Errorf("trace log missing entry %v %v %v %v %v", write, addr, version, length, off) | ||
} | ||
|
||
// checkReadGroup checks that a group of entries is present and verifies the read host address. | ||
// This is useful for checking a batch of concurrent reads are present in the log. | ||
// The tract id itself is embedded in addr (along with the index of the | ||
// tractserver), so we don't bother checking id. | ||
func (log *tsTraceLog) checkReadEntries(t *testing.T, write bool, entries []tsTraceEntry) { | ||
for _, e := range entries { | ||
log.checkRead(t, write, e.addr, e.version, e.length, e.off) | ||
} | ||
} | ||
|
||
// newClient creates a Client suitable for testing. The trace function given | ||
// should return core.NoError for a read/write to proceed, and something else to | ||
// inject an error. | ||
// inject an error. The disableBackupReads parameter allows the backup read feature | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment change should be reverted |
||
// to be turned off when tests only need to send reads to a single tract. | ||
// Note: this mechanism doesn't provide a way to inject an error into a write | ||
// _and_ have the write reflected on the tractserver anyway. We can test that | ||
// later. | ||
|
@@ -86,7 +115,7 @@ func newClient(trace tsTraceFunc) *Client { | |
// newTracingClient creates a Client suitable for testing. It's connected to a | ||
// fake master, three fake curators (each responsible for one partition), and as | ||
// many tractservers as there are tract copies (the fake curator places each | ||
// copy on a separate fake tractserver). The trace log returned can be used to | ||
// copy on a separate fake tractserver). The trace log returned can be used to | ||
// check the exact calls made to each tractserver. | ||
func newTracingClient() (*Client, *tsTraceLog) { | ||
traceLog := new(tsTraceLog) | ||
|
@@ -135,6 +164,13 @@ func testWriteRead(t *testing.T, blob *Blob, length int, off int64) { | |
} | ||
} | ||
|
||
// testWrite does a single write at the given length and offset. | ||
func testWrite(t *testing.T, blob *Blob, length int, off int64) { | ||
blob.Seek(off, os.SEEK_SET) | ||
p1 := makeData(length) | ||
checkWrite(t, blob, p1) | ||
} | ||
|
||
// testWriteReadInParts does a series of sequential writes at the given length | ||
// and offset, and then a series of sequential reads, and checks that the data | ||
// matches. | ||
|
@@ -257,6 +293,83 @@ func TestWriteReadTracedExactlyOneTract(t *testing.T) { | |
trace.checkLength(t, 7) | ||
} | ||
|
||
func (cli *Client) setupBackupClient(maxNumBackups int) chan<- time.Time { | ||
bch := make(chan time.Time) | ||
cli.backupReadState = backupReadState{ | ||
BackupReadBehavior: BackupReadBehavior{ | ||
Enabled: true, | ||
MaxNumBackups: maxNumBackups, | ||
}, | ||
} | ||
cli.backupReadState.backupDelayFunc = func(_ time.Duration) <-chan time.Time { | ||
// send a time value to this channel to unblock. | ||
<-bch | ||
return time.After(0 * time.Second) | ||
} | ||
return bch | ||
} | ||
|
||
func TestWriteReadTracedExactlyOneTractWithBackups(t *testing.T) { | ||
cli, trace := newTracingClient() | ||
bch := cli.setupBackupClient(2) // 2 backup requests | ||
|
||
// Write a blob and check the writes are logged. | ||
blob := createBlob(t, cli) | ||
blob.Seek(core.TractLength, os.SEEK_SET) | ||
p1 := makeData(core.TractLength) | ||
checkWrite(t, blob, p1) | ||
|
||
trace.check(t, true, "ts-0000000100000001:0000-0", 1, 0, 0) | ||
trace.check(t, true, "ts-0000000100000001:0000-1", 1, 0, 0) | ||
trace.check(t, true, "ts-0000000100000001:0000-2", 1, 0, 0) | ||
trace.check(t, true, "ts-0000000100000001:0001-0", 1, core.TractLength, 0) | ||
trace.check(t, true, "ts-0000000100000001:0001-1", 1, core.TractLength, 0) | ||
trace.check(t, true, "ts-0000000100000001:0001-2", 1, core.TractLength, 0) | ||
|
||
// Send a read with backups. And syncronize backup reads using fake time. | ||
go func() { | ||
blob.Seek(core.TractLength, os.SEEK_SET) | ||
p := make([]byte, core.TractLength) | ||
n, err := blob.Read(p) | ||
if err != nil || n != core.TractLength { | ||
t.Fatal("error or short read", n, core.TractLength, err) | ||
} | ||
}() | ||
|
||
// TODO(eric): The test was still racy after using the channel approach. If the first | ||
// read completes, the other two are cancelled. Perhaps we need to mock the cancel func | ||
// as well to disable cancellation? | ||
bch <- time.Time{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, I'm thinking you don't need delayFunc/bch after all |
||
bch <- time.Time{} | ||
time.Sleep(100 * time.Millisecond) | ||
|
||
// Check that all three reads, 1 primary and 2 backup requests are logged. | ||
tsIDs := []tsTraceEntry{ | ||
{addr: "ts-0000000100000001:0001-0", version: 1, length: core.TractLength, off: 0}, | ||
{addr: "ts-0000000100000001:0001-1", version: 1, length: core.TractLength, off: 0}, | ||
{addr: "ts-0000000100000001:0001-2", version: 1, length: core.TractLength, off: 0}, | ||
} | ||
trace.checkReadEntries(t, false, tsIDs) | ||
trace.checkLength(t, 9) | ||
} | ||
|
||
func TestReadFailoverWithBackups(t *testing.T) { | ||
fail := func(e tsTraceEntry) core.Error { | ||
// Reads from the first two tractservers fail. | ||
if !e.write && !strings.HasSuffix(e.addr, "2") { | ||
return core.ErrRPC | ||
} | ||
return core.NoError | ||
} | ||
|
||
cli := newClient(fail) | ||
bch := cli.setupBackupClient(1) // 1 backup requests | ||
go func() { | ||
bch <- time.Time{} | ||
}() | ||
testWriteRead(t, createBlob(t, cli), 3*core.TractLength+8765, 2*core.TractLength+27) | ||
} | ||
|
||
func TestWriteFailSimple(t *testing.T) { | ||
fail := func(e tsTraceEntry) core.Error { | ||
// All writes to last tractserver fail | ||
|
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused?