##// END OF EJS Templates
changegroup: remove reordering control (BC)...
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

File last commit:

r39840:d3d333ab default
r39897:db5501d9 default
Show More
wireprotocolv2.txt
507 lines | 16.4 KiB | text/plain | TextLexer
**Experimental and under active development**
This section documents the wire protocol commands exposed to transports
using the frame-based protocol. The set of commands exposed through
these transports is distinct from the set of commands exposed to legacy
transports.
The frame-based protocol uses CBOR to encode command execution requests.
All command arguments must be mapped to a specific or set of CBOR data
types.
The response to many commands is also CBOR. There is no common response
format: each command defines its own response format.
TODOs
=====
* Add "node namespace" support to each command. In order to support
SHA-1 hash transition, we want servers to be able to expose different
"node namespaces" for the same data. Every command operating on nodes
should specify which "node namespace" it is operating on and responses
should encode the "node namespace" accordingly.
Commands
========
The sections below detail all commands available to wire protocol version
2.
branchmap
---------
Obtain heads in named branches.
Receives no arguments.
The response is a map with bytestring keys defining the branch name.
Values are arrays of bytestring defining raw changeset nodes.
capabilities
------------
Obtain the server's capabilities.
Receives no arguments.
This command is typically called only as part of the handshake during
initial connection establishment.
The response is a map with bytestring keys defining server information.
The defined keys are:
commands
A map defining available wire protocol commands on this server.
Keys in the map are the names of commands that can be invoked. Values
are maps defining information about that command. The bytestring keys
are:
args
(map) Describes arguments accepted by the command.
Keys are bytestrings denoting the argument name.
Values are maps describing the argument. The map has the following
bytestring keys:
default
(varied) The default value for this argument if not specified. Only
present if ``required`` is not true.
required
(boolean) Whether the argument must be specified. Failure to send
required arguments will result in an error executing the command.
type
(bytestring) The type of the argument. e.g. ``bytes`` or ``bool``.
validvalues
(set) Values that are recognized for this argument. Some arguments
only allow a fixed set of values to be specified. These arguments
may advertise that set in this key. If this set is advertised and
a value not in this set is specified, the command should result
in error.
permissions
An array of permissions required to execute this command.
compression
An array of maps defining available compression format support.
The array is sorted from most preferred to least preferred.
Each entry has the following bytestring keys:
name
Name of the compression engine. e.g. ``zstd`` or ``zlib``.
framingmediatypes
An array of bytestrings defining the supported framing protocol
media types. Servers will not accept media types not in this list.
pathfilterprefixes
(set of bytestring) Matcher prefixes that are recognized when performing
path filtering. Specifying a path filter whose type/prefix does not
match one in this set will likely be rejected by the server.
rawrepoformats
An array of storage formats the repository is using. This set of
requirements can be used to determine whether a client can read a
*raw* copy of file data available.
changesetdata
-------------
Obtain various data related to changesets.
The command accepts the following arguments:
noderange
(array of arrays of bytestrings) An array of 2 elements, each being an
array of node bytestrings. The first array denotes the changelog revisions
that are already known to the client. The second array denotes the changelog
revision DAG heads to fetch. The argument essentially defines a DAG range
bounded by root and head nodes to fetch.
The roots array may be empty. The heads array must be defined.
nodes
(array of bytestrings) Changelog revisions to request explicitly.
nodesdepth
(unsigned integer) Number of ancestor revisions of elements in ``nodes``
to also fetch. When defined, for each element in ``nodes``, DAG ancestors
will be walked until at most N total revisions are emitted.
fields
(set of bytestring) Which data associated with changelog revisions to
fetch. The following values are recognized:
bookmarks
Bookmarks associated with a revision.
parents
Parent revisions.
phase
The phase state of a revision.
revision
The raw, revision data for the changelog entry. The hash of this data
will match the revision's node value.
The server resolves the set of revisions relevant to the request by taking
the union of the ``noderange`` and ``nodes`` arguments. At least one of these
arguments must be defined.
The response bytestream starts with a CBOR map describing the data that follows.
This map has the following bytestring keys:
totalitems
(unsigned integer) Total number of changelog revisions whose data is being
transferred. This maps to the set of revisions in the requested node
range, not the total number of records that follow (see below for why).
Following the map header is a series of 0 or more CBOR values. If values
are present, the first value will always be a map describing a single changeset
revision.
If the ``fieldsfollowing`` key is present, the map will immediately be followed
by N CBOR bytestring values, where N is the number of elements in
``fieldsfollowing``. Each bytestring value corresponds to a field denoted
by ``fieldsfollowing``.
Following the optional bytestring field values is the next revision descriptor
map, or end of stream.
Each revision descriptor map has the following bytestring keys:
node
(bytestring) The node value for this revision. This is the SHA-1 hash of
the raw revision data.
bookmarks (optional)
(array of bytestrings) Bookmarks attached to this revision. Only present
if ``bookmarks`` data is being requested and the revision has bookmarks
attached.
fieldsfollowing (optional)
(array of 2-array) Denotes what fields immediately follow this map. Each
value is an array with 2 elements: the bytestring field name and an unsigned
integer describing the length of the data, in bytes.
If this key isn't present, no special fields will follow this map.
The following fields may be present:
revision
Raw, revision data for the changelog entry. Contains a serialized form
of the changeset data, including the author, date, commit message, set
of changed files, manifest node, and other metadata.
Only present if the ``revision`` field was requested.
parents (optional)
(array of bytestrings) The nodes representing the parent revisions of this
revision. Only present if ``parents`` data is being requested.
phase (optional)
(bytestring) The phase that a revision is in. Recognized values are
``secret``, ``draft``, and ``public``. Only present if ``phase`` data
is being requested.
If nodes are requested via ``noderange``, they will be emitted in DAG order,
parents always before children.
If nodes are requested via ``nodes``, they will be emitted in requested order.
Nodes from ``nodes`` are emitted before nodes from ``noderange``.
The set of changeset revisions emitted may not match the exact set of
changesets requested. Furthermore, the set of keys present on each
map may vary. This is to facilitate emitting changeset updates as well
as new revisions.
For example, if the request wants ``phase`` and ``revision`` data,
the response may contain entries for each changeset in the common nodes
set with the ``phase`` key and without the ``revision`` key in order
to reflect a phase-only update.
TODO support different revision selection mechanisms (e.g. non-public, specific
revisions)
TODO support different hash "namespaces" for revisions (e.g. sha-1 versus other)
TODO support emitting obsolescence data
TODO support filtering based on relevant paths (narrow clone)
TODO support hgtagsfnodes cache / tags data
TODO support branch heads cache
TODO consider unify query mechanism. e.g. as an array of "query descriptors"
rather than a set of top-level arguments that have semantics when combined.
filedata
--------
Obtain various data related to an individual tracked file.
The command accepts the following arguments:
fields
(set of bytestring) Which data associated with a file to fetch.
The following values are recognized:
parents
Parent nodes for the revision.
revision
The raw revision data for a file.
haveparents
(bool) Whether the client has the parent revisions of all requested
nodes. If set, the server may emit revision data as deltas against
any parent revision. If not set, the server MUST only emit deltas for
revisions previously emitted by this command.
False is assumed in the absence of any value.
nodes
(array of bytestrings) File nodes whose data to retrieve.
path
(bytestring) Path of the tracked file whose data to retrieve.
TODO allow specifying revisions via alternate means (such as from
changeset revisions or ranges)
The response bytestream starts with a CBOR map describing the data that
follows. It has the following bytestream keys:
totalitems
(unsigned integer) Total number of file revisions whose data is
being returned.
Following the map header is a series of 0 or more CBOR values. If values
are present, the first value will always be a map describing a single changeset
revision.
If the ``fieldsfollowing`` key is present, the map will immediately be followed
by N CBOR bytestring values, where N is the number of elements in
``fieldsfollowing``. Each bytestring value corresponds to a field denoted
by ``fieldsfollowing``.
Following the optional bytestring field values is the next revision descriptor
map, or end of stream.
Each revision descriptor map has the following bytestring keys:
Each map has the following bytestring keys:
node
(bytestring) The node of the file revision whose data is represented.
deltabasenode
(bytestring) Node of the file revision the following delta is against.
Only present if the ``revision`` field is requested and delta data
follows this map.
fieldsfollowing
(array of 2-array) Denotes extra bytestring fields that following this map.
See the documentation for ``changesetdata`` for semantics.
The following named fields may be present:
``delta``
The delta data to use to construct the fulltext revision.
Only present if the ``revision`` field is requested and a delta is
being emitted. The ``deltabasenode`` top-level key will also be
present if this field is being emitted.
``revision``
The fulltext revision data for this manifest. Only present if the
``revision`` field is requested and a fulltext revision is being emitted.
parents
(array of bytestring) The nodes of the parents of this file revision.
Only present if the ``parents`` field is requested.
When ``revision`` data is requested, the server chooses to emit either fulltext
revision data or a delta. What the server decides can be inferred by looking
for the presence of the ``delta`` or ``revision`` keys in the
``fieldsfollowing`` array.
heads
-----
Obtain DAG heads in the repository.
The command accepts the following arguments:
publiconly (optional)
(boolean) If set, operate on the DAG for public phase changesets only.
Non-public (i.e. draft) phase DAG heads will not be returned.
The response is a CBOR array of bytestrings defining changeset nodes
of DAG heads. The array can be empty if the repository is empty or no
changesets satisfied the request.
TODO consider exposing phase of heads in response
known
-----
Determine whether a series of changeset nodes is known to the server.
The command accepts the following arguments:
nodes
(array of bytestrings) List of changeset nodes whose presence to
query.
The response is a bytestring where each byte contains a 0 or 1 for the
corresponding requested node at the same index.
TODO use a bit array for even more compact response
listkeys
--------
List values in a specified ``pushkey`` namespace.
The command receives the following arguments:
namespace
(bytestring) Pushkey namespace to query.
The response is a map with bytestring keys and values.
TODO consider using binary to represent nodes in certain pushkey namespaces.
lookup
------
Try to resolve a value to a changeset revision.
Unlike ``known`` which operates on changeset nodes, lookup operates on
node fragments and other names that a user may use.
The command receives the following arguments:
key
(bytestring) Value to try to resolve.
On success, returns a bytestring containing the resolved node.
manifestdata
------------
Obtain various data related to manifests (which are lists of files in
a revision).
The command accepts the following arguments:
fields
(set of bytestring) Which data associated with manifests to fetch.
The following values are recognized:
parents
Parent nodes for the manifest.
revision
The raw revision data for the manifest.
haveparents
(bool) Whether the client has the parent revisions of all requested
nodes. If set, the server may emit revision data as deltas against
any parent revision. If not set, the server MUST only emit deltas for
revisions previously emitted by this command.
False is assumed in the absence of any value.
nodes
(array of bytestring) Manifest nodes whose data to retrieve.
tree
(bytestring) Path to manifest to retrieve. The empty bytestring represents
the root manifest. All other values represent directories/trees within
the repository.
TODO allow specifying revisions via alternate means (such as from changeset
revisions or ranges)
TODO consider recursive expansion of manifests (with path filtering for
narrow use cases)
The response bytestream starts with a CBOR map describing the data that
follows. It has the following bytestring keys:
totalitems
(unsigned integer) Total number of manifest revisions whose data is
being returned.
Following the map header is a series of 0 or more CBOR values. If values
are present, the first value will always be a map describing a single manifest
revision.
If the ``fieldsfollowing`` key is present, the map will immediately be followed
by N CBOR bytestring values, where N is the number of elements in
``fieldsfollowing``. Each bytestring value corresponds to a field denoted
by ``fieldsfollowing``.
Following the optional bytestring field values is the next revision descriptor
map, or end of stream.
Each revision descriptor map has the following bytestring keys:
node
(bytestring) The node of the manifest revision whose data is represented.
deltabasenode
(bytestring) The node that the delta representation of this revision is
computed against. Only present if the ``revision`` field is requested and
a delta is being emitted.
fieldsfollowing
(array of 2-array) Denotes extra bytestring fields that following this map.
See the documentation for ``changesetdata`` for semantics.
The following named fields may be present:
``delta``
The delta data to use to construct the fulltext revision.
Only present if the ``revision`` field is requested and a delta is
being emitted. The ``deltabasenode`` top-level key will also be
present if this field is being emitted.
``revision``
The fulltext revision data for this manifest. Only present if the
``revision`` field is requested and a fulltext revision is being emitted.
parents
(array of bytestring) The nodes of the parents of this manifest revision.
Only present if the ``parents`` field is requested.
When ``revision`` data is requested, the server chooses to emit either fulltext
revision data or a delta. What the server decides can be inferred by looking
for the presence of ``delta`` or ``revision`` in the ``fieldsfollowing`` array.
pushkey
-------
Set a value using the ``pushkey`` protocol.
The command receives the following arguments:
namespace
(bytestring) Pushkey namespace to operate on.
key
(bytestring) The pushkey key to set.
old
(bytestring) Old value for this key.
new
(bytestring) New value for this key.
TODO consider using binary to represent nodes is certain pushkey namespaces.
TODO better define response type and meaning.