Mon, 24 Sep 2018 12:49:17 -0700 filelog: stop proxying rawsize() (API)
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 12:49:17 -0700] rev 39875
filelog: stop proxying rawsize() (API) This method is no longer used by external consumers. The API is quite low-level and is effectively len(revision(raw=True)). I don't see a compelling reason to keep it around. Let's drop the API and make the file storage interface simpler. Differential Revision: https://phab.mercurial-scm.org/D4750
Mon, 24 Sep 2018 12:42:03 -0700 filelog: stop proxying "opener" (API)
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 12:42:03 -0700] rev 39874
filelog: stop proxying "opener" (API) The last consumer of it in upgrade code was removed as part of the previous commit. This attribute is revlog specific (because it assumes the existence of a vfs for performing I/O on tracked file data) and therefore isn't appropriate for a generic storage interface. So nuke it. Differential Revision: https://phab.mercurial-scm.org/D4749
Mon, 24 Sep 2018 11:16:33 -0700 filelog: stop proxying flags() (API)
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 11:16:33 -0700] rev 39873
filelog: stop proxying flags() (API) Per-revision storage flags are kinda a revlog-centric API. (Except for the fact that changegroup uses the same integer flags as revlog does and there's minimal verification that the server's flags map to the client's storage flags - but that's another problem.) The last user of flags() was in verify.py and that code was just moved into revlog.py and is accessed behind the verifyintegrity() file storage API. Since there are no more consumers, let's drop the proxy and remove the method from the file storage interface. This commit only drops the dedicated API for reading a single revision's storage flags: we still support reading and writing flags through the bulk data retrieval and add revision APIs. And since changegroups encode revlog integer flags over the wire, we'll always need to support flags at some level. The removal of individual storage flags may be too premature. But since flags() is now unused, I'd like to see how far we can get without that dedicated API - especially since it uses revision numbers instead of nodes. Differential Revision: https://phab.mercurial-scm.org/D4746
Mon, 24 Sep 2018 11:27:47 -0700 revlog: move revision verification out of verify
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 11:27:47 -0700] rev 39872
revlog: move revision verification out of verify File revision verification is performing low-level checks of file storage, namely that flags are appropriate and revision data can be resolved. Since these checks are somewhat revlog-specific and may not be appropriate for alternate storage backends, this commit moves those checks from verify.py to revlog.py. Because we're now emitting warnings/errors that apply to specific revisions, we taught the iverifyproblem interface to expose the problematic node and to report this node in verify output. This was necessary to prevent unwanted test changes. After this change, revlog.verifyintegrity() and file verify code in verify.py both iterate over revisions and resolve their fulltext. But they do so in separate loops. (verify.py needs to resolve fulltexts as part of calling renamed() - at least when using revlogs.) This should add overhead. But on the mozilla-unified repo: $ hg verify before: time: real 700.640 secs (user 585.520+0.000 sys 23.480+0.000) after: time: real 682.380 secs (user 570.370+0.000 sys 22.240+0.000) I'm not sure what's going on. Maybe avoiding the filelog attribute proxies shaved off enough time to offset the losses? Maybe fulltext resolution has less overhead than I thought? I've left a comment indicating the potential for optimization. But because it doesn't produce a performance regression on a large repository, I'm not going to worry about it. Differential Revision: https://phab.mercurial-scm.org/D4745
Wed, 26 Sep 2018 12:06:44 -0700 tests: de-flake test-narrow-debugrebuilddirstate.t
Martin von Zweigbergk <martinvonz@google.com> [Wed, 26 Sep 2018 12:06:44 -0700] rev 39871
tests: de-flake test-narrow-debugrebuilddirstate.t If the dirstate gets written much later (usually 1-2 s, depending on FS) than the working copy file (there's only one), then the `hg debugdirstate` command will include a timestamp. There's nothing wrong with that, so we should just allow it. Differential Revision: https://phab.mercurial-scm.org/D4758
Mon, 24 Sep 2018 12:39:34 -0700 upgrade: use storageinfo() for obtaining storage metadata
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 12:39:34 -0700] rev 39870
upgrade: use storageinfo() for obtaining storage metadata Let's switch to our new API for obtaining information about storage. This eliminates the last consumer of rawsize() and the opener proxy from the file storage interface! Differential Revision: https://phab.mercurial-scm.org/D4748
Mon, 24 Sep 2018 11:56:48 -0700 revlog: add method for obtaining storage info (API)
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 11:56:48 -0700] rev 39869
revlog: add method for obtaining storage info (API) We currently have a handful of methods on the file and manifest storage interfaces for obtaining metadata about storage. e.g. files() is used to obtain the files backing storage. rawsize() is to quickly compute the size of tracked revisions without resolving their fulltext. Code in upgrade and stream clone make heavy use of these methods. The existing APIs are generic and don't necessarily have the specialization that we need going forward. For example, files() doesn't distinguish between exclusive storage and shared storage. This makes stream clone difficult to implement when e.g. there may be a single file backing storage for multiple tracked paths. It also makes reporting difficult, as we don't know how many bytes are actually used by storage since we can't easily identify shared files. This commit implements a new method for obtaining storage metadata. It is designed to accept arguments specifying what metadata to request and to return a dict with those fields populated. We /could/ make each of these attributes a separate method. But this is a specialized API and I'm trying to avoid method bloat on the interfaces. There is also the possibility that certain callers will want to obtain multiple fields in different combinations and some backends may have performance issues obtaining all that data via separate method calls. Simple storage integration tests have been added. For now, we assume fields can't be "None" (ignoring the interface documentation). We can revisit this later. Differential Revision: https://phab.mercurial-scm.org/D4747
Wed, 26 Sep 2018 11:27:41 -0700 lfs: drop unused import
Gregory Szorc <gregory.szorc@gmail.com> [Wed, 26 Sep 2018 11:27:41 -0700] rev 39868
lfs: drop unused import A recent change dropped the last user of this module. Differential Revision: https://phab.mercurial-scm.org/D4744
Mon, 24 Sep 2018 10:08:58 -0700 filelog: drop _generaldelta attribute (API)
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 10:08:58 -0700] rev 39867
filelog: drop _generaldelta attribute (API) With changegroup moving to emitrevisions(), this revlog-specific attribute is no longer used and can be deleted. Good riddance. Differential Revision: https://phab.mercurial-scm.org/D4727
Mon, 24 Sep 2018 09:59:19 -0700 revlog: drop emitrevisiondeltas() and associated functionality (API)
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 09:59:19 -0700] rev 39866
revlog: drop emitrevisiondeltas() and associated functionality (API) emitrevisions() is the future! Differential Revision: https://phab.mercurial-scm.org/D4726
Fri, 21 Sep 2018 18:47:04 -0700 changegroup: port to emitrevisions() (issue5976)
Gregory Szorc <gregory.szorc@gmail.com> [Fri, 21 Sep 2018 18:47:04 -0700] rev 39865
changegroup: port to emitrevisions() (issue5976) We now have a unified API for emitting revision data from a storage backend. It handles sorting nodes and the complicated delta versus revision decisions for us. This commit ports changegroup to that API. There should be no behavior changes for changegroups not using ellipsis. And lack of test changes seems to confirm that. There are some changes for ellipsis mode, however. Before, when sending an ellipsis revision, we would always send a fulltext revision (as opposed to a delta). There was a TODO tracking this open item. One of the things the emitrevisions() API does for us is figure out whether we can safely emit a delta. So, it is now possible for ellipsis revisions to be sent as deltas! (It does this by not assuming parent/ancestor revisions are available and tracking which revisions have been sent out.) Because we eliminated the list of revision delta request objects, performance has improved substantially: $ hg perfchangegroupchangelog before: ! wall 24.348077 comb 24.330000 user 24.140000 sys 0.190000 (best of 3) after: ! wall 18.245911 comb 18.240000 user 18.100000 sys 0.140000 (best of 3) That's a lot of overhead for creating a few hundred thousand Python objects! This is still a little slower than 4.7. Probably due to 23d582ca introducing a type for the revision/delta results. There is potentially room to optimize. But at some point we need to abstract storage in order to support alternate storage backends. Unfortunately that means using a Python data structure to represent results. And unfortunately there is overhead with every new Python object created. Differential Revision: https://phab.mercurial-scm.org/D4725
Mon, 24 Sep 2018 09:48:02 -0700 wireprotov2server: port to emitrevisions()
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 09:48:02 -0700] rev 39864
wireprotov2server: port to emitrevisions() We now have a proper storage API to request data on multiple revisions. We can drop it into wire protocol version 2 with minimal effort. The new API handles pretty much everything we were doing manually to build up the delta request. So we were able to delete a lot of code. As a bonus, wireprotov2 code is no longer accessing some low-level storage APIs. This includes the assumption that a node has an associated numeric revision number! This should make it drastically simpler to implement a server that doesn't have the concept of revision numbers. Differential Revision: https://phab.mercurial-scm.org/D4724
Fri, 21 Sep 2018 14:54:59 -0700 tests: use more complex file storage test
Gregory Szorc <gregory.szorc@gmail.com> [Fri, 21 Sep 2018 14:54:59 -0700] rev 39863
tests: use more complex file storage test The previous test was attempting to to test delta storage behavior. It didn't do a very good job at it because there was a good chance a delta wasn't being used in storage. Let's switch the test to yield a delta in storage so an upcoming change to delegate delta logic to storage has the desired effect. Differential Revision: https://phab.mercurial-scm.org/D4723
Fri, 21 Sep 2018 14:28:21 -0700 revlog: new API to emit revision data
Gregory Szorc <gregory.szorc@gmail.com> [Fri, 21 Sep 2018 14:28:21 -0700] rev 39862
revlog: new API to emit revision data I recently refactored changegroup generation code to make it more storage agnostic. I made significant progress. But there is still a bit of work to be done. Specifically: * Changegroup code is looking at low-level storage attributes to influence sorting. Sorting should be done at the storage layer. * The linknode lookup and sorting code for ellipsis is very complicated. * Linknodes are just generally wonky because e.g. file storage doesn't know how to translate a linkrev to a changelog node. * We regressed performance when introducing the request-response objects. Having thought about this problem a bit, I think I've come up with a better interface for emitting revision deltas. This commit defines and implements that interface. See the docstring in repository.py for more info. This API adds 3 notable features over the previous one. First, it defers node ordering to the storage implementation in the common case but allows overriding as necessary. We have a facility for requesting an exact ordering (used in ellipsis mode). We have another facility for storage order (used for changelog). Second, we have an argument specifying assumptions about parents revisions. This can be used to force a fulltext revision when we don't know the receiver has a parent revision to delta against. Third, we can control whether revision data is emitted. This makes the API suitable as a generic "index data retrieval" API as well as for producing revision deltas - possibly in the same operation! The new API is much simpler: we no longer need a complicated "request" object to encapsulate the delta generation request. I'm optimistic this will restore performance loss associated with emitrevisiondeltas(). Storage unit tests for the new API have been implemented. Future commits will port existing consumers of emitrevisiondeltas() to the new API then remove emitrevisiondeltas(). Differential Revision: https://phab.mercurial-scm.org/D4722
Mon, 24 Sep 2018 09:41:42 -0700 changegroup: remove reordering control (BC)
Gregory Szorc <gregory.szorc@gmail.com> [Mon, 24 Sep 2018 09:41:42 -0700] rev 39861
changegroup: remove reordering control (BC) This logic - including the experimental bundle.reorder option - was originally added in a8e3931e3fb5 in 2011 and then later ported to changegroup.py. The intent of this option and associated logic is to control the ordering of revisions in deltagroups in changegroups. At the time it was implemented, only changegroup version 1 existed and generaldelta revlogs were just coming into the world. Changegroup version 1 requires that deltas be made against the last revision sent over the wire. Used with generaldelta, this created an impedance mismatch of sorts and resulted in changegroup producers spending a lot of time recomputing deltas. Revision reordering was introduced so outgoing revisions would be sent in "generaldelta order" and producers would be able to reuse internal deltas from storage. Later on, we introduced changegroup version 2. It supported denoting which revision a delta was against. So we no longer needed to sort outgoing revisions to ensure optimal delta generation from the producer. So, subsequent changegroup versions disabled reordering. We also later made the changelog not store deltas by default. And we also made the changelog send out deltas in storage order. Why we do this for changelog, I'm not sure. Maybe we want to preserve revision order across clones? It doesn't really matter for this commit. Fast forward to 2018. We want to abstract storage backends. And having changegroup code require knowledge about how deltas are stored internally interferes with that goal. This commit removes reordering control from changegroup generation. After this commit, the reordering behavior is: * The changelog is always sent out in storage order (no behavior change). * Non-changelog generaldelta revlogs are reordered to always be in DAG topological order (previously, generaldelta revlogs would be emitted in storage order for version 2 and 3 changegroups). * Non-changelog non-generaldelta revlogs are sent in storage order (no behavior change). * There exists no config option to override behavior. The big difference here is that generaldelta revlogs now *always* have their revisions sorted in DAG order before going out over the wire. This behavior was previously only done for changegroup version 1. Version 2 and version 3 changegroups disabled reordering because the interchange format supported encoding arbitrary delta parents, so reordering wasn't strictly necessary. I can think of a few significant implications for this change. Because changegroup receivers will now see non-changelog revisions in DAG order instead of storage order, the internal storage order of manifests and files may differ substantially between producer and consumer. I don't think this matters that much, since the storage order of manifests and files is largely hidden from users. Only the storage order of changelog matters (because `hg log` shows the changelog in storage order). I don't think there should be any controversy here. The reordering of revisions has implications for changegroup producers. Previously, generaldelta revlogs would be emitted in storage order. And in the common case, the internally-stored delta could effectively be copied from disk into the deltagroup delta. This meant that emitting delta groups for generaldelta revlogs would be mostly linear read I/O. This is desirable for performance. With us now reordering generaldelta revlog revisions in DAG order, the read operations may use more random I/O instead of sequential I/O. This could result in performance loss. But with the prevalence of SSDs and fast random I/O, I'm not too worried. (Note: the optimal emission order for revlogs is actually delta encoding order. But the changegroup code wasn't doing that before or after this change. We could potentially implement that in a later commit.) Changegroups in DAG order will have implications for receivers. Previously, receiving storage order might mean seeing a number of interleaved branches. This would mean long delta chains, sparse I/O, and possibly more fulltext revisions instead of deltas, blowing up storage storage. (This is the same set of problems that sparse revlogs aims to address.) With the producer now sending revisions in DAG order, the receiver also stores revisions in DAG order. That means revisions for the same DAG branch are all grouped together. And this should yield better storage outcomes. In other words, sending the reordered changegroup allows the receiver to have better storage order and for the producer to not propagate its (possibly sub-optimal) internal storage order. On the mozilla-unified repository, this change influences bundle generation: $ hg bundle -t none-v2 -a before: time: real 355.680 secs (user 256.790+0.000 sys 16.820+0.000) after: time: real 382.950 secs (user 281.700+0.000 sys 17.690+0.000) before: 7,150,228,967 bytes (uncompressed) after: 7,041,556,273 bytes (uncompressed) before: 1,669,063,234 bytes (zstd l=3) after: 1,628,598,830 bytes (zstd l=3) $ hg unbundle before: time: real 511.910 secs (user 466.750+0.000 sys 32.680+0.000) after: time: real 487.790 secs (user 443.940+0.000 sys 30.840+0.000) 00manifest.d size: source: 274,924,292 bytes before: 304,741,626 bytes after: 245,252,087 bytes .hg/store total file size: source: 2,649,133,490 before: 2,680,888,130 after: 2,627,875,673 We see the bundle size drop. That's probably because if a revlog internally isn't storing a delta, it will choose to delta against the last emitted revision. And on repos with interleaved branches (like mozilla-unified), the previous revision could be an unrelated branch and therefore be a large delta. But with this patch, the previous revision is likely p1 or p2 and a delta should be small. We also see the manifest size drop by ~50 MB. It's worth noting that the manifest actually *increased* in size by ~25 MB in the old strategy and decreased ~25 MB from its source in the new strategy. Again, my explanation for this is that the DAG ordering in the changegroup is resulting in better grouping of revisions in the receiver, which results in more compact delta chains and higher storage efficiency. Unbundle time also dropped. I suspect this is due to the revlog having to work less to compute deltas since the incoming deltas are more optimal. i.e. the receiver spends less time resolving fulltext revisions as incoming deltas bounce around between DAG branches and delta chains. We also see bundle generation time increase. This is not desirable. However, the regression is only significant on the original repository: if we generate a bundle from the repository created from the new, always reordered bundles, we're close to baseline (if not at it with expected noise): $ hg bundle -t none-v2 -a before (original): time: real 355.680 secs (user 256.790+0.000 sys 16.820+0.000) after (original): time: real 382.950 secs (user 281.700+0.000 sys 17.690+0.000) after (new repo): time: real 362.280 secs (user 260.300+0.000 sys 17.700+0.000) This regression is a bit worrying because it will impact serving canonical repositories (that don't have optimal internal storage unless they are reordered - possibly as part of running `hg debugupgraderepo`). However, this regression will only be noticed by very large changegroups. And I'm guessing/hoping that any repository that large is using clonebundles to mitigate server load. Again, sending DAG order isn't the optimal send order for servers: sending in storage-delta order is. But in order to enable storage-optimal send order, we'll need a storage API that handles sorting. Future commits will introduce such an API. Differential Revision: https://phab.mercurial-scm.org/D4721
Thu, 20 Sep 2018 19:31:07 -0700 filelog: drop index attribute (API)
Gregory Szorc <gregory.szorc@gmail.com> [Thu, 20 Sep 2018 19:31:07 -0700] rev 39860
filelog: drop index attribute (API) The previous commit removed the last consumer of the "index" attribute on the file storage interface. The index is an extremely low-level data structure that is revlog specific and isn't appropriate to expose as part of a generic storage API. There may be a market for an efficient data structure to obtain metadata on every revision for a file. But if there is, it should be designed using e.g. named attributes for lookup instead of a list-like of 8-tuples. Let's drop the attribute from filelog and remove the attribute from the file storage interface. Differential Revision: https://phab.mercurial-scm.org/D4720
(0) -30000 -10000 -3000 -1000 -300 -100 -16 +16 +100 +300 +1000 +3000 +10000 tip