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

Reuse socket #72

Merged
merged 1 commit into from
Nov 17, 2022
Merged

Reuse socket #72

merged 1 commit into from
Nov 17, 2022

Conversation

raphjaph
Copy link
Contributor

This is a quick and dirty attempt at fixing #67 that reuses the socket for multiple requests. It has few issues:

  • no error handing, client becomes unusable after encountering a single error
  • SimpleHttpTransport::request() takes a mut reference to self, which cascades to all downstream users
  • TcpStream is not Clone, therefore I had to remove the derive Clone

An alternate implementation based on reqwest (hyper seems too low-level because of the async stuff) wouldn't have these problems. So I think I'm going to start on that. Just wanted so share this draft PR if anyone has feedback or has an idea on how to do this without reqwest.

@apoelstra
Copy link
Owner

Cool! Thanks for taking this on.

Honestly I think you're close to having a "good" design. A couple small changes would fix your limitations:

  • To get error handling, catch errors and set self.sock to None before returning; now the client can be reused. A later iteration should maybe detect EINTR and retry sending automatically, or something, but we can deal with that later.
  • To avoid &mut and get Clone we could wrap the underlying socket in a Arc<Mutex>...but OTOH the user can do that themselves, which is a minor annoyance and gives the user more freedom.

@raphjaph
Copy link
Contributor Author

Thanks for the tips! Have a look and let me know what you think.

@apoelstra
Copy link
Owner

Could you squash all the commits into one?

src/simple_http.rs Outdated Show resolved Hide resolved
src/simple_http.rs Outdated Show resolved Hide resolved
src/simple_http.rs Outdated Show resolved Hide resolved
Copy link
Owner

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK 9f4ab54

@raphjaph
Copy link
Contributor Author

We tried using this and something went wrong, so definitely don't merge this yet. I'll try to take a look at this over the weekend and figure out what exactly went wrong.

@raphjaph raphjaph force-pushed the reuse-socket branch 2 times, most recently from 697fcb4 to c621b97 Compare October 31, 2022 21:29
@raphjaph
Copy link
Contributor Author

There were two problems:

  1. The Connection: Close HTTP Header was set. This would tell the RPC server to close the connection after it finished writing, so subsequent socket writes would beget broken pipe errors.
  2. We needed to parse the Content-Length Header to know when the response was done.

We tested it with our application and everything works as it should now. So this would also close #67.

Copy link
Collaborator

@tcharding tcharding left a comment

Choose a reason for hiding this comment

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

ACK c621b97

src/simple_http.rs Outdated Show resolved Hide resolved
@tcharding
Copy link
Collaborator

ACK 1826d5e

@tcharding
Copy link
Collaborator

Gentle bump @apoelstra

@apoelstra
Copy link
Owner

Yep, sorry -- let me test resyncing my homebrew wallet against Core with this change and then it can go in.


if response_code == 401 {
// There is no body in a 401 response, so don't try to read it
return Err(Error::HttpErrorCode(response_code));
}

let content_length = content_length.ok_or(Error::HttpParseError)?;

let mut buffer = vec![0; content_length];
Copy link
Owner

Choose a reason for hiding this comment

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

We definitely need to put a limit on this, but it's fine for now :). We'll do a followup PR to audit this because I think get_line may also be subject to memory exhaustion attacks.

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, that sounds prudent

Copy link
Owner

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK 1826d5e

@apoelstra
Copy link
Owner

Hmm, with this PR I see a dramatic (~10x) slowdown in my syncing speed. top shows around 20% CPU usage from each of icboc (my wallet) and bitcoind, i.e. both are only using one core. Vs with the commit before this PR both are close to 50%.

My ACK still stands because we need to fix the Mac bug but we need to investigate this.

@apoelstra
Copy link
Owner

apoelstra commented Nov 17, 2022

Edit: a previous version of this comment said that there was still a 50-100% perf hit. This is because my laptop was unplugged and therefore throttled when I was doing the measurement.

Ok, after modifying this PR to tighten the mutex lock's scope, in particular to unlock it before doing any parsing of the result, I get the same performance as before (possibly a small speedup, but I'm not measuring carefully enough to tell). I'm fine to move that to a followup PR as well.

@tcharding
Copy link
Collaborator

@raphjaph just fyi your CI fails are not your fault, will be resolved by: #74 (comment)

@apoelstra
Copy link
Owner

@raphjaph can you rebase on master? We've fixed the CI.

@raphjaph
Copy link
Contributor Author

Edit: a previous version of this comment said that there was still a 50-100% perf hit. This is because my laptop was unplugged and therefore throttled when I was doing the measurement.

Ok, after modifying this PR to tighten the mutex lock's scope, in particular to unlock it before doing any parsing of the result, I get the same performance as before (possibly a small speedup, but I'm not measuring carefully enough to tell). I'm fine to move that to a followup PR as well.

Ok, that's good to know I'll do the performance stuff in a follow-up PR then.

Copy link
Owner

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK e867374

@apoelstra apoelstra merged commit fe53ba4 into apoelstra:master Nov 17, 2022
apoelstra added a commit that referenced this pull request Nov 28, 2022
2380d90 simple_http: eliminate confused `get_line` function; reuse buffer (Andrew Poelstra)
72bea34 simple_http: deserialize json from the reader, don't use an intermediate buffer (Andrew Poelstra)
c20033e simple_http: add type ascriptions; fix incorrect mutexguard drop logic (Andrew Poelstra)
20da231 simple_http: store the BufReader rather than bare socket (Andrew Poelstra)
8d2ec83 add fuzzing harness for simple_http (Andrew Poelstra)
f44a535 simple_http: mock out TcpStream when fuzzing (Andrew Poelstra)
2a0903e simple_http: handle large or missing Content-length headers correctly (Andrew Poelstra)
e6eaedd simple_http: fix crash when receiving non-ascii HTTP hello (Andrew Poelstra)
7640a4b simple_http: split `HttpParseError` into several variants (Andrew Poelstra)
36708d6 simple_http: tighten mutex lifetime (Andrew Poelstra)

Pull request description:

  Tightens the mutex lifetime to improve performance. Basically we read all the data into a buffer and then unlock the mutex ASAP. We could be a bit more memory-efficient by using `serde_json::from_reader` to parse directly from the socket, but (a) that would make it harder to enforce the Content-length header; and (b) it'd hold the mutex for longer than necessary.

  This commit also splits out the `HttpParseError` into several variants, which unfortunately makes this a breaking change. I think I can move that commit into its own PR if we want to get a minor rev out, but I don't think we care too much about that since this crate is pretty far to the edge of most dependency trees.

  Adds a fuzz harness but doesn't integrate it into CI. Fixes a couple bugs I found while fuzzing locally.

ACKs for top commit:
  raphjaph:
    ACK 2380d90
  tcharding:
    ACK 2380d90

Tree-SHA512: a7462434606f193c0736ebb74cb516d121dbd315db47a82754abb236b47ed744c2655729c407a98613e468f48a2fac025e5ccdab69e1e89395aeb0c2b2dfefca
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