1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
================
Container format
================
Status
======
:Date: 2007-06-07
This document describes the proposed container format for streaming and
storing collections of data in Bazaar. Initially this will be used for
streaming revision data for incremental push/pull in the smart server for
0.18, but the intention is that this will be the basis for much more
than just that use case.
In particular, this document currently focuses almost exclusively on the
streaming case, and not the on-disk storage case. It also does not
discuss the APIs used to manipulate containers and their records.
.. contents::
Motivation
==========
To create a low-level file format which is suitable for solving the smart
server latency problem and whose layout and requirements are extendable in
future versions of Bazaar, and with no requirements that the smart server
does not have today.
Terminology
===========
A **container** is a streamable file that contains a series of
**records**. Records may have **names**, and consist of bytes.
Use Cases
=========
Here's a brief description of use cases this format is intended to
support.
Streaming data between a smart server and client
------------------------------------------------
It would be nice if we could combine multiple containers into a single
stream by something no more expensive than concatenation (e.g. by omitting
end/start marker pairs).
This doesn't imply that such a combination necessarily produces a valid
container (e.g. care must be taken to ensure that names are still unique
in the combined container), or even a useful container. It is simply that
the cost of assembling a new combined container is practically as cheap as
simple concatenation.
Incremental push or pull
~~~~~~~~~~~~~~~~~~~~~~~~
Consider the use case of incremental push/pull, which is currently (0.16)
very slow on high-latency links due to the large number of round trips.
What we'd like is something like the following.
A client will make a request meaning "give me the knit contents for these
revision IDs" (how the client determines which revision IDs it needs is
unimportant here). In response, the server streams a single container of:
* one record per file-id:revision-id knit gzip contents and graph data,
* one record per inventory:revision-id knit gzip contents and graph
data,
* one record per revision knit gzip contents,
* one record per revision signature,
* end marker record.
in that order.
Persistent storage on disk
--------------------------
We want a storage format that allows lock-free writes, which suggests a
format that uses *rename into place*, and *do not modify after writing*.
Usable before deep model changes to Bazaar
------------------------------------------
We want a format we can use and refine sooner rather than later. So it
should be usable before the anticipated model changes for Bazaar "1.0"
land, while not conflicting with those changes either.
Specifically, we'd like to have this format in Bazaar 0.18.
Examples of possible record content
-----------------------------------
* full texts of file versions
* deltas of full texts
* revisions
* inventories
* inventory as tree items e.g. the inventory data for 20 files
* revision signatures
* per-file graph data
* annotation cache
Characteristics
===============
Some key aspects of the described format are discussed in this section.
No length-prefixing of entire container
---------------------------------------
The overall container is not length-prefixed. Instead there is an end
marker so that readers can determine when they have read the entire
container. This also does not conflict with the goal of allowing
single-pass writing.
Structured as a self-contained series of records
------------------------------------------------
The container contains a series of *records*. Each record is
self-delimiting. Record markers are lightweight. The overhead in terms
of bytes and processing for records in this container vs. the raw contents
of those records is minimal.
Addressing records
------------------
There is a requirement that each object can be given an arbitrary name.
Some version control systems address all content by the SHA-1 digest of
that content, but this scheme is unsatisfactory for Bazaar's revision
objects. We can still allow addressing by SHA-1 digest for those content
types where it makes sense.
Some proposed object names:
* to name a revision: "``revision:``\ *revision-id*". e.g.,
`revision:pqm@pqm.ubuntu.com-20070531210833-8ptk86ocu822hjd5`.
* to name an inventory delta: "``inventory.delta:``\ *revision-id*". e.g.,
`inventory.delta:pqm@pqm.ubuntu.com-20070531210833-8ptk86ocu822hjd5`.
It seems likely that we may want to have multiple names for an object.
This format allows that (by allowing multiple ``name`` headers in a Bytes
record).
Although records are in principle addressable by name, this specification
alone doesn't provide for efficient access to a particular record given
its name. It is intended that separate indexes will be maintained to
provide this.
It is acceptable to have records with no explicit name, if the expected
use of them does not require them. For example:
* a record's content could be self-describing in the context of a
particular container, or
* a record could be accessed via an index based on SHA-1, or
* when streaming, the first record could be treated specially.
Reasonably cheap for small records
----------------------------------
The overhead for storing fairly short records (tens of bytes, rather than
thousands or millions) is minimal. The minimum overhead is 3 bytes plus
the length of the decimal representation of the *length* value (for a
record with no name).
Specification
=============
This describes just a basic layer for storing a simple series of
"records". This layer has no intrinsic understanding of the contents of
those records.
The format is:
* a **container lead-in**, "``Bazaar pack format 1 (introduced in 0.18)\n``",
* followed by one or more **records**.
A record is:
* a 1 byte **kind marker**.
* 0 or more bytes of record content, depending on the record type.
Record types
------------
End Marker
~~~~~~~~~~
An **End Marker** record:
* has a kind marker of "``E``",
* no content bytes.
End Marker records signal the end of a container.
Bytes
~~~~~
A **Bytes** record:
* has a kind marker of "``B``",
* followed by a mandatory **content length** [1]_:
"*number*\ ``\n``", where *number* is in decimal, e.g::
1234
* followed by zero or more optional **names**:
"*name*\ ``\n``", e.g.::
revision:pqm@pqm.ubuntu.com-20070531210833-8ptk86ocu822hjd5
* followed by an **end of headers** byte: "``\n``",
* followed by some **bytes**, exactly as many as specified by the length
prefix header.
So a Bytes record is a series of lines encoding the length and names (if
any) followed by a body.
For example, this is a possible Bytes record (including the kind marker)::
B26
example-name1
example-name2
abcdefghijklmnopqrstuvwxyz
Names
-----
Names should be UTF-8 encoded strings, with no whitespace. Names should
be unique within a single container, but no guarantee of uniqueness
outside of the container is made by this layer. Names need to be at least
one character long.
.. [1] This requires that the writer of a record knows the full length of
the record up front, which typically means it will need to buffer an
entire record in memory. For the first version of this format this is
considered to be acceptable.
..
vim: ft=rst tw=74 ai
|