Sat, 20 Jan 2018 22:55:42 -0800 bundle2: increase payload part chunk size to 32kb
Gregory Szorc <gregory.szorc@gmail.com> [Sat, 20 Jan 2018 22:55:42 -0800] rev 35793
bundle2: increase payload part chunk size to 32kb Bundle2 payload parts are framed chunks. Esentially, we obtain data in equal size chunks of size `preferedchunksize` and emit those to a generator. That generator is fed into a compressor (which can be the no-op compressor, which just re-emits the generator). And the output from the compressor likely goes to a file descriptor or socket. What this means is that small chunk sizes create more Python objects and Python function calls than larger chunk sizes. And as we know, Python object and function call overhead in performance sensitive code matters (at least with CPython). This commit increases the bundle2 part payload chunk size from 4k to 32k. Practically speaking, this means that the chunks we feed into a compressor (implemented in C code) or feed directly into a file handle or socket write() are larger. It's possible the chunks might be larger than what the receiver can handle in one logical operation. But at that point, we're in C code, which is much more efficient at dealing with splitting up the chunk and making multiple function calls than Python is. A downside to larger chunks is that the receiver has to wait for that much data to arrive (either raw or from a decompressor) before it can process the chunk. But 32kb still feels like a small buffer to have to wait for. And in many cases, the client will convert from 8 read(4096) to 1 read(32768). That's happening in Python land. So we cut down on the number of Python objects and function calls, making the client faster as well. I don't think there are any significant concerns to increasing the payload chunk size to 32kb. The impact of this change on performance significant. Using `curl` to obtain a stream clone bundle2 payload from a server on localhost serving the mozilla-unified repository: before: 20.78 user; 7.71 system; 80.5 MB/s after: 13.90 user; 3.51 system; 132 MB/s legacy: 9.72 user; 8.16 system; 132 MB/s bundle2 stream clone generation is still more resource intensive than legacy stream clone (that's likely because of the use of a util.chunkbuffer). But the throughput is the same. We might be in territory we're this is effectively a benchmark of the networking stack or Python's syscall throughput. From the client perspective, `hg clone -U --stream`: before: 33.50 user; 7.95 system; 53.3 MB/s after: 22.82 user; 7.33 system; 72.7 MB/s legacy: 29.96 user; 7.94 system; 58.0 MB/s And for `hg clone --stream` with a working directory update of ~230k files: after: 119.55 user; 26.47 system; 0:57.08 wall legacy: 126.98 user; 26.94 system; 1:05.56 wall So, it appears that bundle2's stream clone is now definitively faster than legacy stream clone! Differential Revision: https://phab.mercurial-scm.org/D1932
Mon, 22 Jan 2018 12:23:47 -0800 bundle2: always advertise client support for stream parts
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 22 Jan 2018 12:23:47 -0800] rev 35792
bundle2: always advertise client support for stream parts Previously, enabling of stream clone over bundle2 was a server-side only change. And clients would only advertise bundle2 support for stream clones if an experimental config option was set. This situation wasn't forward compatible because in the future (when the feature is enabled on servers by default), an old client would send a request to the server but it wouldn't send its own bundle2 capability support for stream parts. Servers would have to infer that clients not sending this capability were old Mercurial clients that only sent the capability if the feature was explicitly enabled. Implicit and inferred behavior makes implementing servers hard. It's much better to be explicit about client features. We should either make the config option for bundle2 stream clones disable the feature client-side as well (so a server doesn't see a request from a client not advertising stream support). Or we should always advertise stream support if a client is willing to accept stream parts. Since I anticipating stabilizing stream clone support in bundle2 quickly, I think it's safe to always advertise client support in the bundle2 capabilities. So this commit changes things to do that. Because capabilities now vary between client and server, we had to create variations of the variable substitutions for those strings. Differential Revision: https://phab.mercurial-scm.org/D1931
Mon, 22 Jan 2018 12:22:01 -0800 exchange: don't send stream data when server.uncompressed is set
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 22 Jan 2018 12:22:01 -0800] rev 35791
exchange: don't send stream data when server.uncompressed is set Previously, bundle2 stream support would send out data even though the streaming clone feature was disabled. This commit changes the part handler to respect the server config. Differential Revision: https://phab.mercurial-scm.org/D1930
Mon, 22 Jan 2018 12:21:15 -0800 bundle2: don't advertise stream bundle2 capability when feature disabled
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 22 Jan 2018 12:21:15 -0800] rev 35790
bundle2: don't advertise stream bundle2 capability when feature disabled The server.uncompressed config option can be used to disable streaming clones. While the top-level capability to advertise streaming clone support isn't advertised when this option is set, we were still sending the bundle2-level capabilities advertising support for stream parts. It makes sense to not advertise that support when streaming clones are globally disabled. If the structure of the new code seems a bit odd, it is because we'll have to further tweak the behavior in commit(s) ahead. Differential Revision: https://phab.mercurial-scm.org/D1929
Mon, 22 Jan 2018 12:19:45 -0800 tests: add more testing around server.uncompressed
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 22 Jan 2018 12:19:45 -0800] rev 35789
tests: add more testing around server.uncompressed We already have testing for server.uncompressed in test-http*.t. However, it doesn't cover the new bundle2 use case. And, we don't have comprehensive testing of advertised capabilities. We add tests to test-clone-uncompressed.t that demonstrate behavior for both legacy and bundle2 configurations. If you look closely, the bundle2 capabilities are advertising stream support when it isn't enabled. That's a bug. In addition, while the client is smart enough to not request a stream clone when the server doesn't have the feature enabled, the getbundle wire protocol command is still sending stream clone data. This doesn't match the behavior of the legacy stream_out wire protocol command. That's also a bug. Tests have been added. While I was here, I also changed how the PID is recorded in $DAEMON_PIDS. If we kill a process, the PID formerly in $DAEMON_PIDS no longer exists. So we should replace that file instead of appending to it. Differential Revision: https://phab.mercurial-scm.org/D1928
Mon, 22 Jan 2018 12:19:49 -0800 bundle2: move version of stream clone into part name
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 22 Jan 2018 12:19:49 -0800] rev 35788
bundle2: move version of stream clone into part name I don't like having version numbers as part parameters. It means that parts can theoretically vary wildly in their generation and processing semantics. I think that a named part should have consistent behavior over time. In other words, if you need to introduce new functionality or behavior, that should be expressed by inventing a new bundle2 part, not adding functionality to an existing part. This commit applies this advice to the just-introduced stream clone via bundle2 feature. The "version" part parameter is removed. The name of the bundle2 part is now "stream2" instead of "stream" with "version=v2". Differential Revision: https://phab.mercurial-scm.org/D1927
Mon, 22 Jan 2018 12:12:29 -0800 exchange: send bundle2 stream clones uncompressed
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 22 Jan 2018 12:12:29 -0800] rev 35787
exchange: send bundle2 stream clones uncompressed Stream clones don't compress well. And compression undermines a point of stream clones which is to trade significant CPU reductions by increasing size. Building upon our introduction of metadata to communicate bundle information back to callers of exchange.getbundlechunks(), we add an attribute to the bundler that communicates whether the bundle is best left uncompressed. We return this attribute as part of the bundle metadata. And the wire protocol honors it when determining whether to compress the wire protocol response. The added test demonstrates that the raw result from the wire protocol is not compressed. It also demonstrates that the server will serve stream responses when the feature isn't enabled. We'll address that in another commit. The effect of this change is that server-side CPU usage for bundle2 stream clones is significantly reduced by removing zstd compression. For the mozilla-unified repository: before: 37.69 user 8.01 system after: 27.38 user 7.34 system Assuming things are CPU bound, that ~10s reduction would translate to faster clones on the client. zstd can decompress at >1 GB/s. So the overhead from decompression on the client is small in the grand scheme of things. But if zlib compression were being used, the overhead would be much greater. Differential Revision: https://phab.mercurial-scm.org/D1926
Mon, 22 Jan 2018 12:38:04 -0800 tests: update test to work with Git 2.16
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 22 Jan 2018 12:38:04 -0800] rev 35786
tests: update test to work with Git 2.16 It looks like Git 2.16 removed the "..." from some strings. Glob over those characters in the test output. Differential Revision: https://phab.mercurial-scm.org/D1935
Sat, 20 Jan 2018 13:41:57 -0800 exchange: return bundle info from getbundlechunks() (API)
Gregory Szorc <gregory.szorc@gmail.com> [Sat, 20 Jan 2018 13:41:57 -0800] rev 35785
exchange: return bundle info from getbundlechunks() (API) We generally want a mechanism to pass information about the generated bundle back to callers (in addition to the byte stream). Ideally we would return a bundler from this function and have the caller code to an interface. But the bundling APIs are not great and getbundlechunks() is the best API we have for obtaining bundle contents in a unified manner. We change getbundlechunks() to return a dict that we can use to communicate metadata. We populate that dict with the bundle version number to demonstrate some value. .. api:: exchange.getbundlechunks() now returns a 2-tuple instead of just an iterator. Differential Revision: https://phab.mercurial-scm.org/D1925
Sat, 20 Jan 2018 15:26:31 -0800 exchange: make stream bundle part deterministic
Gregory Szorc <gregory.szorc@gmail.com> [Sat, 20 Jan 2018 15:26:31 -0800] rev 35784
exchange: make stream bundle part deterministic repo.requirements is a set. We need to sort it so the part content is deterministic. Differential Revision: https://phab.mercurial-scm.org/D1924
Sat, 20 Jan 2018 13:54:36 -0800 bundle2: specify what capabilities will be used for
Gregory Szorc <gregory.szorc@gmail.com> [Sat, 20 Jan 2018 13:54:36 -0800] rev 35783
bundle2: specify what capabilities will be used for We currently assume there is a symmetric relationship of bundle2 capabilities between client and server. However, this may not always be the case. We need a bundle2 capability to advertise bundle2 streaming clone support on servers to differentiate it from the existing, legacy streaming clone support. However, servers may wish to disable streaming clone support. If bundle2 capabilities were the same between client and server, a client (which may also be a server) that has disabled streaming clone support would not be able to perform a streaming clone itself! This commit introduces a "role" argument to bundle2.getrepocaps() that explicitly defines the role being performed. This will allow us (and extensions) to alter bundle2 capabilities depending on the operation being performed. Differential Revision: https://phab.mercurial-scm.org/D1923
Sat, 20 Jan 2018 15:43:02 -0800 wireproto: don't compress errors from getbundle()
Gregory Szorc <gregory.szorc@gmail.com> [Sat, 20 Jan 2018 15:43:02 -0800] rev 35782
wireproto: don't compress errors from getbundle() Errors should be small. There's no real need to compress them. Truth be told, there's no good reason to not compress them either. But leaving them uncompressed makes it easier to test failures by looking at the raw HTTP response. This makes it easier for us to write tests. It may make it easier for people writing their own clients. Differential Revision: https://phab.mercurial-scm.org/D1922
Sat, 20 Jan 2018 16:08:07 -0800 tests: teach get-with-headers.py some new tricks
Gregory Szorc <gregory.szorc@gmail.com> [Sat, 20 Jan 2018 16:08:07 -0800] rev 35781
tests: teach get-with-headers.py some new tricks We add the ability to specify arbitrary HTTP request headers and to save the HTTP response body to a file. These will be used in upcoming commits. Differential Revision: https://phab.mercurial-scm.org/D1921
Sat, 20 Jan 2018 14:59:08 -0800 tests: use argparse in get-with-headers.py
Gregory Szorc <gregory.szorc@gmail.com> [Sat, 20 Jan 2018 14:59:08 -0800] rev 35780
tests: use argparse in get-with-headers.py I'm about to add another flag and I don't want to deal with this organic, artisanal argument parser. Differential Revision: https://phab.mercurial-scm.org/D1920
(0) -30000 -10000 -3000 -1000 -300 -100 -14 +14 +100 +300 +1000 +3000 +10000 tip