Skip to content
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

Split blob writer code out of larger PR. #685

Merged
merged 10 commits into from
Aug 16, 2024

Conversation

cody-littley
Copy link
Contributor

@cody-littley cody-littley commented Aug 6, 2024

Why are these changes needed?

This is code broken off from the larger PR #666

This branch contains the "BlobWriter", which manages the writing of blobs for the traffic generator.

	┌------------┐                                       ┌------------┐
	|   writer   |-┐             ┌------------┐          |   reader   |-┐
	└------------┘ |-┐  -------> |  verifier  | -------> └------------┘ |-┐
	  └------------┘ |           └------------┘            └------------┘ |
	    └------------┘                                       └------------┘

Checks

  • I've made sure the lint is passing in this PR.
  • I've made sure the tests are passing. Note that there might be a few flaky tests, in that case, please comment that they are not relevant.
  • Testing Strategy
    • Unit tests
    • Integration tests
    • This PR is not tested :(

@cody-littley cody-littley self-assigned this Aug 6, 2024
@@ -0,0 +1,96 @@
package test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For mocks, you can enforce they adhere to some interface by doing something like this.
Ex. var _ clients.DisperserClient = (*mockDisperserClient)(nil)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat trick! I actually noticed a few similar lines in other files and was confused by them.

var previousData []byte

for i := 0; i < 100; i++ {
if rand.Float64() < errorProbability {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of testing the error cases probabilistically, why don't we have an explicit test case that tests the failures?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this to non-random, it now generates an error every tenth cycle: if i%10 == 0. Would you still like to se a stand alone unit test for the error case, or is this sufficient?


// These methods should be called exactly once per tick if there are no errors.
// In the presence of errors, nothing should be passed to the unconfirmed key handler.
assert.Equal(t, uint(i+1), disperserClient.DisperseCount)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also directly match if disperserClient.DisperseBlob method has been called by disperserClient.AssertCalled(...) or disperserClient.AssertNumberOfCalls(...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've switched over to your recommended style for mock objects.

Comment on lines 93 to 101
tu.AssertEventuallyTrue(t, func() bool {
lock.Lock()
defer lock.Unlock()
return int(disperserClient.DisperseCount) > i && int(unconfirmedKeyHandler.Count)+errorCount > i
}, time.Second)

// These methods should be called exactly once per tick if there are no errors.
// In the presence of errors, nothing should be passed to the unconfirmed key handler.
assert.Equal(t, uint(i+1), disperserClient.DisperseCount)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timing/synchronizing with another goroutine might be tricky just using time. Does this reliably pass? How do you know if L101 will execute after disperser request has been made?
Maybe one thing we could do is make run method public and run test against this method? That way we can assert expectations after the method returns.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empirically, this was reliably passing (ran it in a loop a few thousand times). Primary reason why I was going through all the trouble of doing it in an attempt to put the tests in another directory without making methods public that shouldn't be public.

I understand your general concern though. I refactored this so that I don't rely on timing. As a result, the test files will end up being in the same package as the other files. Ugly to me, but it seems like a fairly normal practice in go.

Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Copy link
Contributor

@ian-shim ian-shim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, left few more comments for you to consider before merging

disperser clients.DisperserClient

// Unconfirmed keys are sent here.
unconfirmedKeyHandler KeyHandler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have you considered passing unconfirmed keys to a channel instead and letting the caller handle it?
I think that's a common pattern in Go

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm willing to make this change, but since this modifies the API, would it be ok with you if I made this change in the next PR that will add the code that consumes the unconfirmed keys? It will cause some merge conflicts if I change this API in this PR.

data := make([]byte, writer.config.DataSize)
_, err := rand.Read(data)
if err != nil {
panic(fmt.Sprintf("unable to read random data: %s", err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return an error instead of panicking?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

})
if err != nil {
writer.writeFailureMetric.Increment()
writer.logger.Error("failed to send blob request", "err:", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no : postfix on labels

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}

// sendRequest sends a blob to a disperser.
func (writer *BlobWriter) sendRequest(data []byte) ([]byte /* key */, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could use a named return values if the returns values look ambiguous
...sendRequest(data []byte) (key []byte, err error)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, today I learned that syntax exists. :) Change made.

Signed-off-by: Cody Littley <[email protected]>
"fmt"
"github.com/Layr-Labs/eigenda/api/clients"
"github.com/Layr-Labs/eigenda/encoding/utils/codec"
config2 "github.com/Layr-Labs/eigenda/tools/traffic/config"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this alias needed? There seems no collision

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I blame my IDE, it automatically aliased this when I did an automatic refactor. 🙃

Fixed.

package workers

// KeyHandler is an interface describing an object that can accept unconfirmed keys.
type KeyHandler interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit abstract. Is it intended just for unconfirmed blobs? If so, it would be more readable to name these as BlobHandler/AddUncofirmedBlob etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will go away in the next PR when I implement a request from @ian-shim to use a channel to pass this data. I'm intentionally not changing it in this PR to reduce the number of merge conflicts in my feature branch.

// Start begins the blob writer goroutine.
func (writer *BlobWriter) Start() {
writer.waitGroup.Add(1)
ticker := time.NewTicker(writer.config.WriteRequestInterval)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this support a write frequency like 0.2 blobs/sec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This is the period, aka the time in-between writes. If you set a period of 5 seconds, on average it will write 1/5 or 0.2 blobs per second.

Signed-off-by: Cody Littley <[email protected]>
@cody-littley cody-littley merged commit f07e364 into Layr-Labs:master Aug 16, 2024
6 checks passed
ian-shim pushed a commit to ian-shim/eigenda that referenced this pull request Aug 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants