Skip to content
This repository has been archived by the owner on Apr 18, 2024. It is now read-only.

Fix bugs, refactor, document and land the weighted consistent hashing branch #26

Merged
merged 12 commits into from
Feb 17, 2023

Conversation

aarshkshah1992
Copy link
Contributor

@aarshkshah1992 aarshkshah1992 commented Feb 15, 2023

This PR aims to land #19 and make some changes on top of it.

The main changes include:

Closes #27 Closes #25

@aarshkshah1992 aarshkshah1992 changed the title [WIP] Fix bugs, refactor and document the weighted consistent hashing branch [WIP] Fix bugs, refactor, document and land the weighted consistent hashing branch Feb 15, 2023
@aarshkshah1992 aarshkshah1992 marked this pull request as draft February 15, 2023 09:25
@aarshkshah1992 aarshkshah1992 removed the request for review from hacdias February 15, 2023 09:25
// trigger early refreshes when pool size drops below this low watermark
PoolLowWatermark int
MaxConcurrency int
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 is never used.

PoolFailureDownvoteDebounce time.Duration
PoolMaxSize int
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 is never used.

m.Unlock()
nm := NewMember(m.url)
nm.replication = updateWeightF(m.replication)
nm.lastUpdate = time.Now()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need to update the lastUpdate time on this newly constructed member and not on the old member as the pool will now be updated with this member and we want to retain the last updated time across weight updates.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

i think change it on both - if a couple things time out simultaneously against the previous member, we want only one of them to do the update re-weighting

// it owns and runs. We should probably just forget about the Saturn endpoints that were
// previously in the pool but are no longer being returned by the orchestrator. It's highly
// likely that the Orchestrator has deemed them to be non-functional/malicious.
// Let's just override the old pool with the new endpoints returned here.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@willscott @lidel

The orchestrator periodically tests the L1s to mantain a list of responsive L1s.

Copy link
Contributor

@lidel lidel Feb 15, 2023

Choose a reason for hiding this comment

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

I've asked for access to https://github.com/filecoin-saturn/orchestrator to see what exactly is "checked".

I don't think we can trust Orchestrator to give us usefull L1s, we need to verify ourselves.

The tests Orchestrator performs are not end-to-end, use hardcoded CIDs, and if the first try fails, they allow cached result (so nodes that lost retrieval capability, but have test CIDs in cache are never penalized):

Orchestrator opinion would be useful only if it would run a real IPFS node, add random data to IPFS, announce it on DHT, and then attempt to retrieve it via specific L1.

This means "nearby" has little value.
We need to fetch as many L1s we can, and then let the weighting logic to find the most useful ones.
Currently, we only get 25, but with https://github.com/filecoin-saturn/orchestrator/issues/83 we will be able to ask for more.

@aarshkshah1992 update: we can now use https://orchestrator.strn.pl/nodes/nearby?count=9999 to grab as many L1s as we need.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@lidel This make sense to me. I think you'll have to make this change in Bifrost GW as caboose uses whatever orchestrator API URL you pass to it. Caboose Config param you'll have to change is called OrchestratorEndpoint.

Copy link
Contributor

Choose a reason for hiding this comment

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

i think mostly this was: what happens if we get an empty list from the orchestrator because it has a bug / looses it's DB?
it doesn't seem to hurt much to keep using known, working nodes.
maybe we ask for some way for the orchestrator to more forcefully tell us to replace?

Copy link
Contributor

@lidel lidel Feb 16, 2023

Choose a reason for hiding this comment

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

We could be asking for ?count=9999 and see all of them, and decrease weight of ones that were removed.

This way if Orchestrator returns an empty list, of only 4 for some reason, we are not left with a broken client – weights will be applied to all, so no net impact on orchestrator hiccups/misconfigurations.

pool.go Outdated
fb = time.Now()
code = resp.StatusCode
proto = resp.Proto
respReq = resp.Request
defer resp.Body.Close()

// TODO: What if the Saturn node is malicious ? We should have an upper bound on how many bytes we read here.
rb, err := io.ReadAll(resp.Body)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@willscott @lidel Is this a concern ?

Copy link
Contributor

@lidel lidel Feb 15, 2023

Choose a reason for hiding this comment

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

@aarshkshah1992 yes, for block response, we don't expect blocks bigger than 4MiB (bitswap limit). Use io.LimitedReader to cap the amount of data read here, just to be safe. (and error if they send anything bigger)

While at it, there is no point in reading body or calcualting hash for responses other than HTTP 200.
Check if resp.StatusCode != http.StatusOK early, and fail fast, saving CPU and time.

Returning different error for timeout and different one for resp.StatusCode != http.StatusOK will allow us to apply different penalty (timeout means node is trying to find content, HTTP error is just a hard fail).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

PR update with both these changes.

@aarshkshah1992 aarshkshah1992 changed the title [WIP] Fix bugs, refactor, document and land the weighted consistent hashing branch Fix bugs, refactor, document and land the weighted consistent hashing branch Feb 15, 2023
@aarshkshah1992 aarshkshah1992 requested review from willscott, hacdias and lidel and removed request for hacdias February 15, 2023 12:50
@aarshkshah1992 aarshkshah1992 self-assigned this Feb 15, 2023
@aarshkshah1992 aarshkshah1992 marked this pull request as ready for review February 15, 2023 12:56
lidel added a commit to ipfs-inactive/bifrost-gateway that referenced this pull request Feb 15, 2023
lidel added a commit to ipfs-inactive/bifrost-gateway that referenced this pull request Feb 15, 2023
pool.go Show resolved Hide resolved
@@ -38,7 +38,7 @@ func main1() int {
}
out := args.Get(1)

oe, _ := url.Parse("https://orchestrator.strn.pl/nodes/nearby")
oe, _ := url.Parse("https://orchestrator.strn.pl/nodes/nearby?count=100")
Copy link
Contributor

Choose a reason for hiding this comment

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

This parameter should be moved inside of caboose asa a const, appended to the endpoint.
You don't want to ask bifrost-gateway to change URL every time you want to adjust strategy for refreshing L1s :)

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 made the entire OrchestratorEndpoint param an optional value where the default will be this URL with a 1000 nodes. So, you can totally skip passing this param from Bifrost.

pool.go Outdated Show resolved Hide resolved
if received > 0 && received <= maxBlockSize {
buf = buf[:received]
} else {
return nil, ErrBackendFailed
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be a separate error – otherwise we will bang our heads why things do not work N years from now, if ever the block size policy changes (but fine to do it in #28)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah will do it in #28

aarshkshah1992 and others added 2 commits February 16, 2023 18:06
Co-authored-by: Marcin Rataj <lidel@lidel.org>
@aarshkshah1992
Copy link
Contributor Author

aarshkshah1992 commented Feb 16, 2023

@willscott Test is failing because we moved the Saturn client to use https and looks like it does NOT trust the cert given by the httptest TLS server. I'll take a look but a test against the Saturn network runs fine.

@lidel
Copy link
Contributor

lidel commented Feb 16, 2023

@aarshkshah1992 I think this is why we have https://github.com/ipfs/bifrost-gateway/blob/main/blockstore.go#L82-L84

If you move this hack into caboose it should fix your cmd and help us clean up Saturn-related hack from bifrost-gateway
(but still apply the outer RoundTrripper, so user-agent override done in bifrost-gatewat still works)

m.Unlock()
nm := NewMember(m.url)
nm.replication = updateWeightF(m.replication)
nm.lastUpdate = time.Now()
Copy link
Contributor

Choose a reason for hiding this comment

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

i think change it on both - if a couple things time out simultaneously against the previous member, we want only one of them to do the update re-weighting

// it owns and runs. We should probably just forget about the Saturn endpoints that were
// previously in the pool but are no longer being returned by the orchestrator. It's highly
// likely that the Orchestrator has deemed them to be non-functional/malicious.
// Let's just override the old pool with the new endpoints returned here.
Copy link
Contributor

Choose a reason for hiding this comment

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

i think mostly this was: what happens if we get an empty list from the orchestrator because it has a bug / looses it's DB?
it doesn't seem to hurt much to keep using known, working nodes.
maybe we ask for some way for the orchestrator to more forcefully tell us to replace?

@lidel
Copy link
Contributor

lidel commented Feb 16, 2023

@aarshkshah1992 I've fixed go test in d2749d3, will now take it for a spin in bifrost-gateway.

lidel added a commit to ipfs-inactive/bifrost-gateway that referenced this pull request Feb 16, 2023
lidel added a commit to ipfs-inactive/bifrost-gateway that referenced this pull request Feb 16, 2023
Copy link
Contributor

@lidel lidel left a comment

Choose a reason for hiding this comment

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

233eb8a was able to retrieve bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi, I am switching bifrost-gateway to this commit: ipfs-inactive/bifrost-gateway#39

@willscott ok to merge?

@lidel lidel mentioned this pull request Feb 16, 2023
pool.go Show resolved Hide resolved
@willscott willscott merged commit 6180140 into weighted Feb 17, 2023
@willscott willscott deleted the feat/fix-document-weighted branch February 17, 2023 15:35
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants