##// END OF EJS Templates
persistent-nodemap: introduce a test to highlight possible race...
marmoute -
r48852:52018f8e stable
parent child Browse files
Show More
@@ -0,0 +1,294 b''
1 """Create the race condition for issue6554
2
3 The persistent nodemap issues had an issue where a second writer could
4 overwrite the data that a previous write just wrote. The would break the append
5 only garantee of the persistent nodemap and could confuse reader. This
6 extensions create all the necessary synchronisation point to the race condition
7 to happen.
8
9 It involves 3 process <LEFT> (a writer) <RIGHT> (a writer) and <READER>
10
11 [1] <LEFT> take the lock and start a transaction
12 [2] <LEFT> updated `00changelog.i` with the new data
13 [3] <RIGHT> reads:
14 - the new changelog index `00changelog.i`
15 - the old `00changelog.n`
16 [4] <LEFT> update the persistent nodemap:
17 - writing new data from the last valid offset
18 - updating the docket (00changelog.n)
19 [5] <LEFT> release the lock
20 [6] <RIGHT> grab the lock and run `repo.invalidate`
21 [7] <READER> reads:
22 - the changelog index after <LEFT> write
23 - the nodemap docket after <LEFT> write
24 [8] <RIGHT> reload the changelog since `00changelog.n` changed
25 /!\ This is the faulty part in issue 6554, the outdated docket is kept
26 [9] <RIGHT> write:
27 - the changelog index (00changelog.i)
28 - the nodemap data (00changelog*.nd)
29 /!\ if the outdated docket is used, the write starts from the same ofset
30 /!\ as in [4], overwriting data that <LEFT> wrote in step [4].
31 - the nodemap docket (00changelog.n)
32 [10] <READER> reads the nodemap data from `00changelog*.nd`
33 /!\ if step [9] was wrong, the data matching the docket that <READER>
34 /!\ loaded have been overwritten and the expected root-nodes is no longer
35 /!\ valid.
36 """
37
38 from __future__ import print_function
39
40 import os
41
42 from mercurial.revlogutils.constants import KIND_CHANGELOG
43
44 from mercurial import (
45 changelog,
46 encoding,
47 extensions,
48 localrepo,
49 node,
50 pycompat,
51 registrar,
52 testing,
53 util,
54 )
55
56 from mercurial.revlogutils import (
57 nodemap as nodemaputil,
58 )
59
60 configtable = {}
61 configitem = registrar.configitem(configtable)
62
63 configitem(b'devel', b'nodemap-race.role', default=None)
64
65 cmdtable = {}
66 command = registrar.command(cmdtable)
67
68 LEFT = b'left'
69 RIGHT = b'right'
70 READER = b'reader'
71
72 SYNC_DIR = os.path.join(encoding.environ[b'TESTTMP'], b'sync-files')
73
74 # mark the end of step [1]
75 FILE_LEFT_LOCKED = os.path.join(SYNC_DIR, b'left-locked')
76 # mark that step [3] is ready to run.
77 FILE_RIGHT_READY_TO_LOCK = os.path.join(SYNC_DIR, b'right-ready-to-lock')
78
79 # mark the end of step [2]
80 FILE_LEFT_CL_DATA_WRITE = os.path.join(SYNC_DIR, b'left-data')
81 # mark the end of step [4]
82 FILE_LEFT_CL_NODEMAP_WRITE = os.path.join(SYNC_DIR, b'left-nodemap')
83 # mark the end of step [3]
84 FILE_RIGHT_CL_NODEMAP_READ = os.path.join(SYNC_DIR, b'right-nodemap')
85 # mark that step [9] is read to run
86 FILE_RIGHT_CL_NODEMAP_PRE_WRITE = os.path.join(
87 SYNC_DIR, b'right-pre-nodemap-write'
88 )
89 # mark that step [9] has run.
90 FILE_RIGHT_CL_NODEMAP_POST_WRITE = os.path.join(
91 SYNC_DIR, b'right-post-nodemap-write'
92 )
93 # mark that step [7] is ready to run
94 FILE_READER_READY = os.path.join(SYNC_DIR, b'reader-ready')
95 # mark that step [7] has run
96 FILE_READER_READ_DOCKET = os.path.join(SYNC_DIR, b'reader-read-docket')
97
98
99 def _print(*args, **kwargs):
100 print(*args, **kwargs)
101
102
103 def _role(repo):
104 """find the role associated with the process"""
105 return repo.ui.config(b'devel', b'nodemap-race.role')
106
107
108 def wrap_changelog_finalize(orig, cl, tr):
109 """wrap the update of `00changelog.i` during transaction finalization
110
111 This is useful for synchronisation before or after the file is updated on disk.
112 """
113 role = getattr(tr, '_race_role', None)
114 if role == RIGHT:
115 print('right ready to write, waiting for reader')
116 testing.wait_file(FILE_READER_READY)
117 testing.write_file(FILE_RIGHT_CL_NODEMAP_PRE_WRITE)
118 testing.wait_file(FILE_READER_READ_DOCKET)
119 print('right proceeding with writing its changelog index and nodemap')
120 ret = orig(cl, tr)
121 print("finalized changelog write")
122 if role == LEFT:
123 testing.write_file(FILE_LEFT_CL_DATA_WRITE)
124 return ret
125
126
127 def wrap_persist_nodemap(orig, tr, revlog, *args, **kwargs):
128 """wrap the update of `00changelog.n` and `*.nd` during tr finalization
129
130 This is useful for synchronisation before or after the files are updated on
131 disk.
132 """
133 is_cl = revlog.target[0] == KIND_CHANGELOG
134 role = getattr(tr, '_race_role', None)
135 if is_cl:
136 if role == LEFT:
137 testing.wait_file(FILE_RIGHT_CL_NODEMAP_READ)
138 if is_cl:
139 print("persisting changelog nodemap")
140 print(" new data start at", revlog._nodemap_docket.data_length)
141 ret = orig(tr, revlog, *args, **kwargs)
142 if is_cl:
143 print("persisted changelog nodemap")
144 print_nodemap_details(revlog)
145 if role == LEFT:
146 testing.write_file(FILE_LEFT_CL_NODEMAP_WRITE)
147 elif role == RIGHT:
148 testing.write_file(FILE_RIGHT_CL_NODEMAP_POST_WRITE)
149 return ret
150
151
152 def print_nodemap_details(cl):
153 """print relevant information about the nodemap docket currently in memory"""
154 dkt = cl._nodemap_docket
155 print('docket-details:')
156 if dkt is None:
157 print(' <no-docket>')
158 return
159 print(' uid: ', pycompat.sysstr(dkt.uid))
160 print(' actual-tip: ', cl.tiprev())
161 print(' tip-rev: ', dkt.tip_rev)
162 print(' data-length:', dkt.data_length)
163
164
165 def wrap_persisted_data(orig, revlog):
166 """print some information about the nodemap information we just read
167
168 Used by the <READER> process only.
169 """
170 ret = orig(revlog)
171 if ret is not None:
172 docket, data = ret
173 file_path = nodemaputil._rawdata_filepath(revlog, docket)
174 file_path = revlog.opener.join(file_path)
175 file_size = os.path.getsize(file_path)
176 print('record-data-length:', docket.data_length)
177 print('actual-data-length:', len(data))
178 print('file-actual-length:', file_size)
179 return ret
180
181
182 def sync_read(orig):
183 """used by <READER> to force the race window
184
185 This make sure we read the docker from <LEFT> while reading the datafile
186 after <RIGHT> write.
187 """
188 orig()
189 testing.write_file(FILE_READER_READ_DOCKET)
190 print('reader: nodemap docket read')
191 testing.wait_file(FILE_RIGHT_CL_NODEMAP_POST_WRITE)
192
193
194 def uisetup(ui):
195 class RacedRepo(localrepo.localrepository):
196 def lock(self, wait=True):
197 # make sure <RIGHT> as the "Wrong" information in memory before
198 # grabbing the lock
199 newlock = self._currentlock(self._lockref) is None
200 if newlock and _role(self) == LEFT:
201 cl = self.unfiltered().changelog
202 print_nodemap_details(cl)
203 elif newlock and _role(self) == RIGHT:
204 testing.write_file(FILE_RIGHT_READY_TO_LOCK)
205 print('nodemap-race: right side start of the locking sequence')
206 testing.wait_file(FILE_LEFT_LOCKED)
207 testing.wait_file(FILE_LEFT_CL_DATA_WRITE)
208 self.invalidate(clearfilecache=True)
209 print('nodemap-race: right side reading changelog')
210 cl = self.unfiltered().changelog
211 tiprev = cl.tiprev()
212 tip = cl.node(tiprev)
213 tiprev2 = cl.rev(tip)
214 if tiprev != tiprev2:
215 raise RuntimeError(
216 'bad tip -round-trip %d %d' % (tiprev, tiprev2)
217 )
218 testing.write_file(FILE_RIGHT_CL_NODEMAP_READ)
219 print('nodemap-race: right side reading of changelog is done')
220 print_nodemap_details(cl)
221 testing.wait_file(FILE_LEFT_CL_NODEMAP_WRITE)
222 print('nodemap-race: right side ready to wait for the lock')
223 ret = super(RacedRepo, self).lock(wait=wait)
224 if newlock and _role(self) == LEFT:
225 print('nodemap-race: left side locked and ready to commit')
226 testing.write_file(FILE_LEFT_LOCKED)
227 testing.wait_file(FILE_RIGHT_READY_TO_LOCK)
228 cl = self.unfiltered().changelog
229 print_nodemap_details(cl)
230 elif newlock and _role(self) == RIGHT:
231 print('nodemap-race: right side locked and ready to commit')
232 cl = self.unfiltered().changelog
233 print_nodemap_details(cl)
234 return ret
235
236 def transaction(self, *args, **kwargs):
237 # duck punch the role on the transaction to help other pieces of code
238 tr = super(RacedRepo, self).transaction(*args, **kwargs)
239 tr._race_role = _role(self)
240 return tr
241
242 localrepo.localrepository = RacedRepo
243
244 extensions.wrapfunction(
245 nodemaputil, 'persist_nodemap', wrap_persist_nodemap
246 )
247 extensions.wrapfunction(
248 changelog.changelog, '_finalize', wrap_changelog_finalize
249 )
250
251
252 def reposetup(ui, repo):
253 if _role(repo) == READER:
254 extensions.wrapfunction(
255 nodemaputil, 'persisted_data', wrap_persisted_data
256 )
257 extensions.wrapfunction(nodemaputil, 'test_race_hook_1', sync_read)
258
259 class ReaderRepo(repo.__class__):
260 @util.propertycache
261 def changelog(self):
262 print('reader ready to read the changelog, waiting for right')
263 testing.write_file(FILE_READER_READY)
264 testing.wait_file(FILE_RIGHT_CL_NODEMAP_PRE_WRITE)
265 return super(ReaderRepo, self).changelog
266
267 repo.__class__ = ReaderRepo
268
269
270 @command(b'check-nodemap-race')
271 def cmd_check_nodemap_race(ui, repo):
272 """Run proper <READER> access in the race Windows and check nodemap content"""
273 repo = repo.unfiltered()
274 print('reader: reading changelog')
275 cl = repo.changelog
276 print('reader: changelog read')
277 print_nodemap_details(cl)
278 tip_rev = cl.tiprev()
279 tip_node = cl.node(tip_rev)
280 print('tip-rev: ', tip_rev)
281 print('tip-node:', node.short(tip_node).decode('ascii'))
282 print('node-rev:', cl.rev(tip_node))
283 for r in cl.revs():
284 n = cl.node(r)
285 try:
286 r2 = cl.rev(n)
287 except ValueError as exc:
288 print('error while checking revision:', r)
289 print(' ', exc)
290 return 1
291 else:
292 if r2 != r:
293 print('revision %d is missing from the nodemap' % r)
294 return 1
@@ -1,647 +1,657 b''
1 # nodemap.py - nodemap related code and utilities
1 # nodemap.py - nodemap related code and utilities
2 #
2 #
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net>
4 # Copyright 2019 George Racinet <georges.racinet@octobus.net>
4 # Copyright 2019 George Racinet <georges.racinet@octobus.net>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12 import re
12 import re
13 import struct
13 import struct
14
14
15 from ..node import hex
15 from ..node import hex
16
16
17 from .. import (
17 from .. import (
18 error,
18 error,
19 util,
19 util,
20 )
20 )
21 from . import docket as docket_mod
21 from . import docket as docket_mod
22
22
23
23
24 class NodeMap(dict):
24 class NodeMap(dict):
25 def __missing__(self, x):
25 def __missing__(self, x):
26 raise error.RevlogError(b'unknown node: %s' % x)
26 raise error.RevlogError(b'unknown node: %s' % x)
27
27
28
28
29 def test_race_hook_1():
30 """hook point for test
31
32 This let tests to have things happens between the docket reading and the
33 data reading"""
34 pass
35
36
29 def persisted_data(revlog):
37 def persisted_data(revlog):
30 """read the nodemap for a revlog from disk"""
38 """read the nodemap for a revlog from disk"""
31 if revlog._nodemap_file is None:
39 if revlog._nodemap_file is None:
32 return None
40 return None
33 pdata = revlog.opener.tryread(revlog._nodemap_file)
41 pdata = revlog.opener.tryread(revlog._nodemap_file)
34 if not pdata:
42 if not pdata:
35 return None
43 return None
36 offset = 0
44 offset = 0
37 (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size])
45 (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size])
38 if version != ONDISK_VERSION:
46 if version != ONDISK_VERSION:
39 return None
47 return None
40 offset += S_VERSION.size
48 offset += S_VERSION.size
41 headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
49 headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
42 uid_size, tip_rev, data_length, data_unused, tip_node_size = headers
50 uid_size, tip_rev, data_length, data_unused, tip_node_size = headers
43 offset += S_HEADER.size
51 offset += S_HEADER.size
44 docket = NodeMapDocket(pdata[offset : offset + uid_size])
52 docket = NodeMapDocket(pdata[offset : offset + uid_size])
45 offset += uid_size
53 offset += uid_size
46 docket.tip_rev = tip_rev
54 docket.tip_rev = tip_rev
47 docket.tip_node = pdata[offset : offset + tip_node_size]
55 docket.tip_node = pdata[offset : offset + tip_node_size]
48 docket.data_length = data_length
56 docket.data_length = data_length
49 docket.data_unused = data_unused
57 docket.data_unused = data_unused
50
58
51 filename = _rawdata_filepath(revlog, docket)
59 filename = _rawdata_filepath(revlog, docket)
52 use_mmap = revlog.opener.options.get(b"persistent-nodemap.mmap")
60 use_mmap = revlog.opener.options.get(b"persistent-nodemap.mmap")
61
62 test_race_hook_1()
53 try:
63 try:
54 with revlog.opener(filename) as fd:
64 with revlog.opener(filename) as fd:
55 if use_mmap:
65 if use_mmap:
56 try:
66 try:
57 data = util.buffer(util.mmapread(fd, data_length))
67 data = util.buffer(util.mmapread(fd, data_length))
58 except ValueError:
68 except ValueError:
59 # raised when the read file is too small
69 # raised when the read file is too small
60 data = b''
70 data = b''
61 else:
71 else:
62 data = fd.read(data_length)
72 data = fd.read(data_length)
63 except (IOError, OSError) as e:
73 except (IOError, OSError) as e:
64 if e.errno == errno.ENOENT:
74 if e.errno == errno.ENOENT:
65 return None
75 return None
66 else:
76 else:
67 raise
77 raise
68 if len(data) < data_length:
78 if len(data) < data_length:
69 return None
79 return None
70 return docket, data
80 return docket, data
71
81
72
82
73 def setup_persistent_nodemap(tr, revlog):
83 def setup_persistent_nodemap(tr, revlog):
74 """Install whatever is needed transaction side to persist a nodemap on disk
84 """Install whatever is needed transaction side to persist a nodemap on disk
75
85
76 (only actually persist the nodemap if this is relevant for this revlog)
86 (only actually persist the nodemap if this is relevant for this revlog)
77 """
87 """
78 if revlog._inline:
88 if revlog._inline:
79 return # inlined revlog are too small for this to be relevant
89 return # inlined revlog are too small for this to be relevant
80 if revlog._nodemap_file is None:
90 if revlog._nodemap_file is None:
81 return # we do not use persistent_nodemap on this revlog
91 return # we do not use persistent_nodemap on this revlog
82
92
83 # we need to happen after the changelog finalization, in that use "cl-"
93 # we need to happen after the changelog finalization, in that use "cl-"
84 callback_id = b"nm-revlog-persistent-nodemap-%s" % revlog._nodemap_file
94 callback_id = b"nm-revlog-persistent-nodemap-%s" % revlog._nodemap_file
85 if tr.hasfinalize(callback_id):
95 if tr.hasfinalize(callback_id):
86 return # no need to register again
96 return # no need to register again
87 tr.addpending(
97 tr.addpending(
88 callback_id, lambda tr: persist_nodemap(tr, revlog, pending=True)
98 callback_id, lambda tr: persist_nodemap(tr, revlog, pending=True)
89 )
99 )
90 tr.addfinalize(callback_id, lambda tr: persist_nodemap(tr, revlog))
100 tr.addfinalize(callback_id, lambda tr: persist_nodemap(tr, revlog))
91
101
92
102
93 class _NoTransaction(object):
103 class _NoTransaction(object):
94 """transaction like object to update the nodemap outside a transaction"""
104 """transaction like object to update the nodemap outside a transaction"""
95
105
96 def __init__(self):
106 def __init__(self):
97 self._postclose = {}
107 self._postclose = {}
98
108
99 def addpostclose(self, callback_id, callback_func):
109 def addpostclose(self, callback_id, callback_func):
100 self._postclose[callback_id] = callback_func
110 self._postclose[callback_id] = callback_func
101
111
102 def registertmp(self, *args, **kwargs):
112 def registertmp(self, *args, **kwargs):
103 pass
113 pass
104
114
105 def addbackup(self, *args, **kwargs):
115 def addbackup(self, *args, **kwargs):
106 pass
116 pass
107
117
108 def add(self, *args, **kwargs):
118 def add(self, *args, **kwargs):
109 pass
119 pass
110
120
111 def addabort(self, *args, **kwargs):
121 def addabort(self, *args, **kwargs):
112 pass
122 pass
113
123
114 def _report(self, *args):
124 def _report(self, *args):
115 pass
125 pass
116
126
117
127
118 def update_persistent_nodemap(revlog):
128 def update_persistent_nodemap(revlog):
119 """update the persistent nodemap right now
129 """update the persistent nodemap right now
120
130
121 To be used for updating the nodemap on disk outside of a normal transaction
131 To be used for updating the nodemap on disk outside of a normal transaction
122 setup (eg, `debugupdatecache`).
132 setup (eg, `debugupdatecache`).
123 """
133 """
124 if revlog._inline:
134 if revlog._inline:
125 return # inlined revlog are too small for this to be relevant
135 return # inlined revlog are too small for this to be relevant
126 if revlog._nodemap_file is None:
136 if revlog._nodemap_file is None:
127 return # we do not use persistent_nodemap on this revlog
137 return # we do not use persistent_nodemap on this revlog
128
138
129 notr = _NoTransaction()
139 notr = _NoTransaction()
130 persist_nodemap(notr, revlog)
140 persist_nodemap(notr, revlog)
131 for k in sorted(notr._postclose):
141 for k in sorted(notr._postclose):
132 notr._postclose[k](None)
142 notr._postclose[k](None)
133
143
134
144
135 def delete_nodemap(tr, repo, revlog):
145 def delete_nodemap(tr, repo, revlog):
136 """Delete nodemap data on disk for a given revlog"""
146 """Delete nodemap data on disk for a given revlog"""
137 if revlog._nodemap_file is None:
147 if revlog._nodemap_file is None:
138 msg = "calling persist nodemap on a revlog without the feature enabled"
148 msg = "calling persist nodemap on a revlog without the feature enabled"
139 raise error.ProgrammingError(msg)
149 raise error.ProgrammingError(msg)
140 repo.svfs.unlink(revlog._nodemap_file)
150 repo.svfs.unlink(revlog._nodemap_file)
141
151
142
152
143 def persist_nodemap(tr, revlog, pending=False, force=False):
153 def persist_nodemap(tr, revlog, pending=False, force=False):
144 """Write nodemap data on disk for a given revlog"""
154 """Write nodemap data on disk for a given revlog"""
145 if getattr(revlog, 'filteredrevs', ()):
155 if getattr(revlog, 'filteredrevs', ()):
146 raise error.ProgrammingError(
156 raise error.ProgrammingError(
147 "cannot persist nodemap of a filtered changelog"
157 "cannot persist nodemap of a filtered changelog"
148 )
158 )
149 if revlog._nodemap_file is None:
159 if revlog._nodemap_file is None:
150 if force:
160 if force:
151 revlog._nodemap_file = get_nodemap_file(revlog)
161 revlog._nodemap_file = get_nodemap_file(revlog)
152 else:
162 else:
153 msg = "calling persist nodemap on a revlog without the feature enabled"
163 msg = "calling persist nodemap on a revlog without the feature enabled"
154 raise error.ProgrammingError(msg)
164 raise error.ProgrammingError(msg)
155
165
156 can_incremental = util.safehasattr(revlog.index, "nodemap_data_incremental")
166 can_incremental = util.safehasattr(revlog.index, "nodemap_data_incremental")
157 ondisk_docket = revlog._nodemap_docket
167 ondisk_docket = revlog._nodemap_docket
158 feed_data = util.safehasattr(revlog.index, "update_nodemap_data")
168 feed_data = util.safehasattr(revlog.index, "update_nodemap_data")
159 use_mmap = revlog.opener.options.get(b"persistent-nodemap.mmap")
169 use_mmap = revlog.opener.options.get(b"persistent-nodemap.mmap")
160
170
161 data = None
171 data = None
162 # first attemp an incremental update of the data
172 # first attemp an incremental update of the data
163 if can_incremental and ondisk_docket is not None:
173 if can_incremental and ondisk_docket is not None:
164 target_docket = revlog._nodemap_docket.copy()
174 target_docket = revlog._nodemap_docket.copy()
165 (
175 (
166 src_docket,
176 src_docket,
167 data_changed_count,
177 data_changed_count,
168 data,
178 data,
169 ) = revlog.index.nodemap_data_incremental()
179 ) = revlog.index.nodemap_data_incremental()
170 new_length = target_docket.data_length + len(data)
180 new_length = target_docket.data_length + len(data)
171 new_unused = target_docket.data_unused + data_changed_count
181 new_unused = target_docket.data_unused + data_changed_count
172 if src_docket != target_docket:
182 if src_docket != target_docket:
173 data = None
183 data = None
174 elif new_length <= (new_unused * 10): # under 10% of unused data
184 elif new_length <= (new_unused * 10): # under 10% of unused data
175 data = None
185 data = None
176 else:
186 else:
177 datafile = _rawdata_filepath(revlog, target_docket)
187 datafile = _rawdata_filepath(revlog, target_docket)
178 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
188 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
179 # store vfs
189 # store vfs
180 tr.add(datafile, target_docket.data_length)
190 tr.add(datafile, target_docket.data_length)
181 with revlog.opener(datafile, b'r+') as fd:
191 with revlog.opener(datafile, b'r+') as fd:
182 fd.seek(target_docket.data_length)
192 fd.seek(target_docket.data_length)
183 fd.write(data)
193 fd.write(data)
184 if feed_data:
194 if feed_data:
185 if use_mmap:
195 if use_mmap:
186 fd.seek(0)
196 fd.seek(0)
187 new_data = fd.read(new_length)
197 new_data = fd.read(new_length)
188 else:
198 else:
189 fd.flush()
199 fd.flush()
190 new_data = util.buffer(util.mmapread(fd, new_length))
200 new_data = util.buffer(util.mmapread(fd, new_length))
191 target_docket.data_length = new_length
201 target_docket.data_length = new_length
192 target_docket.data_unused = new_unused
202 target_docket.data_unused = new_unused
193
203
194 if data is None:
204 if data is None:
195 # otherwise fallback to a full new export
205 # otherwise fallback to a full new export
196 target_docket = NodeMapDocket()
206 target_docket = NodeMapDocket()
197 datafile = _rawdata_filepath(revlog, target_docket)
207 datafile = _rawdata_filepath(revlog, target_docket)
198 if util.safehasattr(revlog.index, "nodemap_data_all"):
208 if util.safehasattr(revlog.index, "nodemap_data_all"):
199 data = revlog.index.nodemap_data_all()
209 data = revlog.index.nodemap_data_all()
200 else:
210 else:
201 data = persistent_data(revlog.index)
211 data = persistent_data(revlog.index)
202 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
212 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
203 # store vfs
213 # store vfs
204
214
205 tryunlink = revlog.opener.tryunlink
215 tryunlink = revlog.opener.tryunlink
206
216
207 def abortck(tr):
217 def abortck(tr):
208 tryunlink(datafile)
218 tryunlink(datafile)
209
219
210 callback_id = b"delete-%s" % datafile
220 callback_id = b"delete-%s" % datafile
211
221
212 # some flavor of the transaction abort does not cleanup new file, it
222 # some flavor of the transaction abort does not cleanup new file, it
213 # simply empty them.
223 # simply empty them.
214 tr.addabort(callback_id, abortck)
224 tr.addabort(callback_id, abortck)
215 with revlog.opener(datafile, b'w+') as fd:
225 with revlog.opener(datafile, b'w+') as fd:
216 fd.write(data)
226 fd.write(data)
217 if feed_data:
227 if feed_data:
218 if use_mmap:
228 if use_mmap:
219 new_data = data
229 new_data = data
220 else:
230 else:
221 fd.flush()
231 fd.flush()
222 new_data = util.buffer(util.mmapread(fd, len(data)))
232 new_data = util.buffer(util.mmapread(fd, len(data)))
223 target_docket.data_length = len(data)
233 target_docket.data_length = len(data)
224 target_docket.tip_rev = revlog.tiprev()
234 target_docket.tip_rev = revlog.tiprev()
225 target_docket.tip_node = revlog.node(target_docket.tip_rev)
235 target_docket.tip_node = revlog.node(target_docket.tip_rev)
226 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
236 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
227 # store vfs
237 # store vfs
228 file_path = revlog._nodemap_file
238 file_path = revlog._nodemap_file
229 if pending:
239 if pending:
230 file_path += b'.a'
240 file_path += b'.a'
231 tr.registertmp(file_path)
241 tr.registertmp(file_path)
232 else:
242 else:
233 tr.addbackup(file_path)
243 tr.addbackup(file_path)
234
244
235 with revlog.opener(file_path, b'w', atomictemp=True) as fp:
245 with revlog.opener(file_path, b'w', atomictemp=True) as fp:
236 fp.write(target_docket.serialize())
246 fp.write(target_docket.serialize())
237 revlog._nodemap_docket = target_docket
247 revlog._nodemap_docket = target_docket
238 if feed_data:
248 if feed_data:
239 revlog.index.update_nodemap_data(target_docket, new_data)
249 revlog.index.update_nodemap_data(target_docket, new_data)
240
250
241 # search for old index file in all cases, some older process might have
251 # search for old index file in all cases, some older process might have
242 # left one behind.
252 # left one behind.
243 olds = _other_rawdata_filepath(revlog, target_docket)
253 olds = _other_rawdata_filepath(revlog, target_docket)
244 if olds:
254 if olds:
245 realvfs = getattr(revlog, '_realopener', revlog.opener)
255 realvfs = getattr(revlog, '_realopener', revlog.opener)
246
256
247 def cleanup(tr):
257 def cleanup(tr):
248 for oldfile in olds:
258 for oldfile in olds:
249 realvfs.tryunlink(oldfile)
259 realvfs.tryunlink(oldfile)
250
260
251 callback_id = b"revlog-cleanup-nodemap-%s" % revlog._nodemap_file
261 callback_id = b"revlog-cleanup-nodemap-%s" % revlog._nodemap_file
252 tr.addpostclose(callback_id, cleanup)
262 tr.addpostclose(callback_id, cleanup)
253
263
254
264
255 ### Nodemap docket file
265 ### Nodemap docket file
256 #
266 #
257 # The nodemap data are stored on disk using 2 files:
267 # The nodemap data are stored on disk using 2 files:
258 #
268 #
259 # * a raw data files containing a persistent nodemap
269 # * a raw data files containing a persistent nodemap
260 # (see `Nodemap Trie` section)
270 # (see `Nodemap Trie` section)
261 #
271 #
262 # * a small "docket" file containing medatadata
272 # * a small "docket" file containing medatadata
263 #
273 #
264 # While the nodemap data can be multiple tens of megabytes, the "docket" is
274 # While the nodemap data can be multiple tens of megabytes, the "docket" is
265 # small, it is easy to update it automatically or to duplicated its content
275 # small, it is easy to update it automatically or to duplicated its content
266 # during a transaction.
276 # during a transaction.
267 #
277 #
268 # Multiple raw data can exist at the same time (The currently valid one and a
278 # Multiple raw data can exist at the same time (The currently valid one and a
269 # new one beind used by an in progress transaction). To accomodate this, the
279 # new one beind used by an in progress transaction). To accomodate this, the
270 # filename hosting the raw data has a variable parts. The exact filename is
280 # filename hosting the raw data has a variable parts. The exact filename is
271 # specified inside the "docket" file.
281 # specified inside the "docket" file.
272 #
282 #
273 # The docket file contains information to find, qualify and validate the raw
283 # The docket file contains information to find, qualify and validate the raw
274 # data. Its content is currently very light, but it will expand as the on disk
284 # data. Its content is currently very light, but it will expand as the on disk
275 # nodemap gains the necessary features to be used in production.
285 # nodemap gains the necessary features to be used in production.
276
286
277 ONDISK_VERSION = 1
287 ONDISK_VERSION = 1
278 S_VERSION = struct.Struct(">B")
288 S_VERSION = struct.Struct(">B")
279 S_HEADER = struct.Struct(">BQQQQ")
289 S_HEADER = struct.Struct(">BQQQQ")
280
290
281
291
282 class NodeMapDocket(object):
292 class NodeMapDocket(object):
283 """metadata associated with persistent nodemap data
293 """metadata associated with persistent nodemap data
284
294
285 The persistent data may come from disk or be on their way to disk.
295 The persistent data may come from disk or be on their way to disk.
286 """
296 """
287
297
288 def __init__(self, uid=None):
298 def __init__(self, uid=None):
289 if uid is None:
299 if uid is None:
290 uid = docket_mod.make_uid()
300 uid = docket_mod.make_uid()
291 # a unique identifier for the data file:
301 # a unique identifier for the data file:
292 # - When new data are appended, it is preserved.
302 # - When new data are appended, it is preserved.
293 # - When a new data file is created, a new identifier is generated.
303 # - When a new data file is created, a new identifier is generated.
294 self.uid = uid
304 self.uid = uid
295 # the tipmost revision stored in the data file. This revision and all
305 # the tipmost revision stored in the data file. This revision and all
296 # revision before it are expected to be encoded in the data file.
306 # revision before it are expected to be encoded in the data file.
297 self.tip_rev = None
307 self.tip_rev = None
298 # the node of that tipmost revision, if it mismatch the current index
308 # the node of that tipmost revision, if it mismatch the current index
299 # data the docket is not valid for the current index and should be
309 # data the docket is not valid for the current index and should be
300 # discarded.
310 # discarded.
301 #
311 #
302 # note: this method is not perfect as some destructive operation could
312 # note: this method is not perfect as some destructive operation could
303 # preserve the same tip_rev + tip_node while altering lower revision.
313 # preserve the same tip_rev + tip_node while altering lower revision.
304 # However this multiple other caches have the same vulnerability (eg:
314 # However this multiple other caches have the same vulnerability (eg:
305 # brancmap cache).
315 # brancmap cache).
306 self.tip_node = None
316 self.tip_node = None
307 # the size (in bytes) of the persisted data to encode the nodemap valid
317 # the size (in bytes) of the persisted data to encode the nodemap valid
308 # for `tip_rev`.
318 # for `tip_rev`.
309 # - data file shorter than this are corrupted,
319 # - data file shorter than this are corrupted,
310 # - any extra data should be ignored.
320 # - any extra data should be ignored.
311 self.data_length = None
321 self.data_length = None
312 # the amount (in bytes) of "dead" data, still in the data file but no
322 # the amount (in bytes) of "dead" data, still in the data file but no
313 # longer used for the nodemap.
323 # longer used for the nodemap.
314 self.data_unused = 0
324 self.data_unused = 0
315
325
316 def copy(self):
326 def copy(self):
317 new = NodeMapDocket(uid=self.uid)
327 new = NodeMapDocket(uid=self.uid)
318 new.tip_rev = self.tip_rev
328 new.tip_rev = self.tip_rev
319 new.tip_node = self.tip_node
329 new.tip_node = self.tip_node
320 new.data_length = self.data_length
330 new.data_length = self.data_length
321 new.data_unused = self.data_unused
331 new.data_unused = self.data_unused
322 return new
332 return new
323
333
324 def __cmp__(self, other):
334 def __cmp__(self, other):
325 if self.uid < other.uid:
335 if self.uid < other.uid:
326 return -1
336 return -1
327 if self.uid > other.uid:
337 if self.uid > other.uid:
328 return 1
338 return 1
329 elif self.data_length < other.data_length:
339 elif self.data_length < other.data_length:
330 return -1
340 return -1
331 elif self.data_length > other.data_length:
341 elif self.data_length > other.data_length:
332 return 1
342 return 1
333 return 0
343 return 0
334
344
335 def __eq__(self, other):
345 def __eq__(self, other):
336 return self.uid == other.uid and self.data_length == other.data_length
346 return self.uid == other.uid and self.data_length == other.data_length
337
347
338 def serialize(self):
348 def serialize(self):
339 """return serialized bytes for a docket using the passed uid"""
349 """return serialized bytes for a docket using the passed uid"""
340 data = []
350 data = []
341 data.append(S_VERSION.pack(ONDISK_VERSION))
351 data.append(S_VERSION.pack(ONDISK_VERSION))
342 headers = (
352 headers = (
343 len(self.uid),
353 len(self.uid),
344 self.tip_rev,
354 self.tip_rev,
345 self.data_length,
355 self.data_length,
346 self.data_unused,
356 self.data_unused,
347 len(self.tip_node),
357 len(self.tip_node),
348 )
358 )
349 data.append(S_HEADER.pack(*headers))
359 data.append(S_HEADER.pack(*headers))
350 data.append(self.uid)
360 data.append(self.uid)
351 data.append(self.tip_node)
361 data.append(self.tip_node)
352 return b''.join(data)
362 return b''.join(data)
353
363
354
364
355 def _rawdata_filepath(revlog, docket):
365 def _rawdata_filepath(revlog, docket):
356 """The (vfs relative) nodemap's rawdata file for a given uid"""
366 """The (vfs relative) nodemap's rawdata file for a given uid"""
357 prefix = revlog.radix
367 prefix = revlog.radix
358 return b"%s-%s.nd" % (prefix, docket.uid)
368 return b"%s-%s.nd" % (prefix, docket.uid)
359
369
360
370
361 def _other_rawdata_filepath(revlog, docket):
371 def _other_rawdata_filepath(revlog, docket):
362 prefix = revlog.radix
372 prefix = revlog.radix
363 pattern = re.compile(br"(^|/)%s-[0-9a-f]+\.nd$" % prefix)
373 pattern = re.compile(br"(^|/)%s-[0-9a-f]+\.nd$" % prefix)
364 new_file_path = _rawdata_filepath(revlog, docket)
374 new_file_path = _rawdata_filepath(revlog, docket)
365 new_file_name = revlog.opener.basename(new_file_path)
375 new_file_name = revlog.opener.basename(new_file_path)
366 dirpath = revlog.opener.dirname(new_file_path)
376 dirpath = revlog.opener.dirname(new_file_path)
367 others = []
377 others = []
368 for f in revlog.opener.listdir(dirpath):
378 for f in revlog.opener.listdir(dirpath):
369 if pattern.match(f) and f != new_file_name:
379 if pattern.match(f) and f != new_file_name:
370 others.append(f)
380 others.append(f)
371 return others
381 return others
372
382
373
383
374 ### Nodemap Trie
384 ### Nodemap Trie
375 #
385 #
376 # This is a simple reference implementation to compute and persist a nodemap
386 # This is a simple reference implementation to compute and persist a nodemap
377 # trie. This reference implementation is write only. The python version of this
387 # trie. This reference implementation is write only. The python version of this
378 # is not expected to be actually used, since it wont provide performance
388 # is not expected to be actually used, since it wont provide performance
379 # improvement over existing non-persistent C implementation.
389 # improvement over existing non-persistent C implementation.
380 #
390 #
381 # The nodemap is persisted as Trie using 4bits-address/16-entries block. each
391 # The nodemap is persisted as Trie using 4bits-address/16-entries block. each
382 # revision can be adressed using its node shortest prefix.
392 # revision can be adressed using its node shortest prefix.
383 #
393 #
384 # The trie is stored as a sequence of block. Each block contains 16 entries
394 # The trie is stored as a sequence of block. Each block contains 16 entries
385 # (signed 64bit integer, big endian). Each entry can be one of the following:
395 # (signed 64bit integer, big endian). Each entry can be one of the following:
386 #
396 #
387 # * value >= 0 -> index of sub-block
397 # * value >= 0 -> index of sub-block
388 # * value == -1 -> no value
398 # * value == -1 -> no value
389 # * value < -1 -> encoded revision: rev = -(value+2)
399 # * value < -1 -> encoded revision: rev = -(value+2)
390 #
400 #
391 # See REV_OFFSET and _transform_rev below.
401 # See REV_OFFSET and _transform_rev below.
392 #
402 #
393 # The implementation focus on simplicity, not on performance. A Rust
403 # The implementation focus on simplicity, not on performance. A Rust
394 # implementation should provide a efficient version of the same binary
404 # implementation should provide a efficient version of the same binary
395 # persistence. This reference python implementation is never meant to be
405 # persistence. This reference python implementation is never meant to be
396 # extensively use in production.
406 # extensively use in production.
397
407
398
408
399 def persistent_data(index):
409 def persistent_data(index):
400 """return the persistent binary form for a nodemap for a given index"""
410 """return the persistent binary form for a nodemap for a given index"""
401 trie = _build_trie(index)
411 trie = _build_trie(index)
402 return _persist_trie(trie)
412 return _persist_trie(trie)
403
413
404
414
405 def update_persistent_data(index, root, max_idx, last_rev):
415 def update_persistent_data(index, root, max_idx, last_rev):
406 """return the incremental update for persistent nodemap from a given index"""
416 """return the incremental update for persistent nodemap from a given index"""
407 changed_block, trie = _update_trie(index, root, last_rev)
417 changed_block, trie = _update_trie(index, root, last_rev)
408 return (
418 return (
409 changed_block * S_BLOCK.size,
419 changed_block * S_BLOCK.size,
410 _persist_trie(trie, existing_idx=max_idx),
420 _persist_trie(trie, existing_idx=max_idx),
411 )
421 )
412
422
413
423
414 S_BLOCK = struct.Struct(">" + ("l" * 16))
424 S_BLOCK = struct.Struct(">" + ("l" * 16))
415
425
416 NO_ENTRY = -1
426 NO_ENTRY = -1
417 # rev 0 need to be -2 because 0 is used by block, -1 is a special value.
427 # rev 0 need to be -2 because 0 is used by block, -1 is a special value.
418 REV_OFFSET = 2
428 REV_OFFSET = 2
419
429
420
430
421 def _transform_rev(rev):
431 def _transform_rev(rev):
422 """Return the number used to represent the rev in the tree.
432 """Return the number used to represent the rev in the tree.
423
433
424 (or retrieve a rev number from such representation)
434 (or retrieve a rev number from such representation)
425
435
426 Note that this is an involution, a function equal to its inverse (i.e.
436 Note that this is an involution, a function equal to its inverse (i.e.
427 which gives the identity when applied to itself).
437 which gives the identity when applied to itself).
428 """
438 """
429 return -(rev + REV_OFFSET)
439 return -(rev + REV_OFFSET)
430
440
431
441
432 def _to_int(hex_digit):
442 def _to_int(hex_digit):
433 """turn an hexadecimal digit into a proper integer"""
443 """turn an hexadecimal digit into a proper integer"""
434 return int(hex_digit, 16)
444 return int(hex_digit, 16)
435
445
436
446
437 class Block(dict):
447 class Block(dict):
438 """represent a block of the Trie
448 """represent a block of the Trie
439
449
440 contains up to 16 entry indexed from 0 to 15"""
450 contains up to 16 entry indexed from 0 to 15"""
441
451
442 def __init__(self):
452 def __init__(self):
443 super(Block, self).__init__()
453 super(Block, self).__init__()
444 # If this block exist on disk, here is its ID
454 # If this block exist on disk, here is its ID
445 self.ondisk_id = None
455 self.ondisk_id = None
446
456
447 def __iter__(self):
457 def __iter__(self):
448 return iter(self.get(i) for i in range(16))
458 return iter(self.get(i) for i in range(16))
449
459
450
460
451 def _build_trie(index):
461 def _build_trie(index):
452 """build a nodemap trie
462 """build a nodemap trie
453
463
454 The nodemap stores revision number for each unique prefix.
464 The nodemap stores revision number for each unique prefix.
455
465
456 Each block is a dictionary with keys in `[0, 15]`. Values are either
466 Each block is a dictionary with keys in `[0, 15]`. Values are either
457 another block or a revision number.
467 another block or a revision number.
458 """
468 """
459 root = Block()
469 root = Block()
460 for rev in range(len(index)):
470 for rev in range(len(index)):
461 current_hex = hex(index[rev][7])
471 current_hex = hex(index[rev][7])
462 _insert_into_block(index, 0, root, rev, current_hex)
472 _insert_into_block(index, 0, root, rev, current_hex)
463 return root
473 return root
464
474
465
475
466 def _update_trie(index, root, last_rev):
476 def _update_trie(index, root, last_rev):
467 """consume"""
477 """consume"""
468 changed = 0
478 changed = 0
469 for rev in range(last_rev + 1, len(index)):
479 for rev in range(last_rev + 1, len(index)):
470 current_hex = hex(index[rev][7])
480 current_hex = hex(index[rev][7])
471 changed += _insert_into_block(index, 0, root, rev, current_hex)
481 changed += _insert_into_block(index, 0, root, rev, current_hex)
472 return changed, root
482 return changed, root
473
483
474
484
475 def _insert_into_block(index, level, block, current_rev, current_hex):
485 def _insert_into_block(index, level, block, current_rev, current_hex):
476 """insert a new revision in a block
486 """insert a new revision in a block
477
487
478 index: the index we are adding revision for
488 index: the index we are adding revision for
479 level: the depth of the current block in the trie
489 level: the depth of the current block in the trie
480 block: the block currently being considered
490 block: the block currently being considered
481 current_rev: the revision number we are adding
491 current_rev: the revision number we are adding
482 current_hex: the hexadecimal representation of the of that revision
492 current_hex: the hexadecimal representation of the of that revision
483 """
493 """
484 changed = 1
494 changed = 1
485 if block.ondisk_id is not None:
495 if block.ondisk_id is not None:
486 block.ondisk_id = None
496 block.ondisk_id = None
487 hex_digit = _to_int(current_hex[level : level + 1])
497 hex_digit = _to_int(current_hex[level : level + 1])
488 entry = block.get(hex_digit)
498 entry = block.get(hex_digit)
489 if entry is None:
499 if entry is None:
490 # no entry, simply store the revision number
500 # no entry, simply store the revision number
491 block[hex_digit] = current_rev
501 block[hex_digit] = current_rev
492 elif isinstance(entry, dict):
502 elif isinstance(entry, dict):
493 # need to recurse to an underlying block
503 # need to recurse to an underlying block
494 changed += _insert_into_block(
504 changed += _insert_into_block(
495 index, level + 1, entry, current_rev, current_hex
505 index, level + 1, entry, current_rev, current_hex
496 )
506 )
497 else:
507 else:
498 # collision with a previously unique prefix, inserting new
508 # collision with a previously unique prefix, inserting new
499 # vertices to fit both entry.
509 # vertices to fit both entry.
500 other_hex = hex(index[entry][7])
510 other_hex = hex(index[entry][7])
501 other_rev = entry
511 other_rev = entry
502 new = Block()
512 new = Block()
503 block[hex_digit] = new
513 block[hex_digit] = new
504 _insert_into_block(index, level + 1, new, other_rev, other_hex)
514 _insert_into_block(index, level + 1, new, other_rev, other_hex)
505 _insert_into_block(index, level + 1, new, current_rev, current_hex)
515 _insert_into_block(index, level + 1, new, current_rev, current_hex)
506 return changed
516 return changed
507
517
508
518
509 def _persist_trie(root, existing_idx=None):
519 def _persist_trie(root, existing_idx=None):
510 """turn a nodemap trie into persistent binary data
520 """turn a nodemap trie into persistent binary data
511
521
512 See `_build_trie` for nodemap trie structure"""
522 See `_build_trie` for nodemap trie structure"""
513 block_map = {}
523 block_map = {}
514 if existing_idx is not None:
524 if existing_idx is not None:
515 base_idx = existing_idx + 1
525 base_idx = existing_idx + 1
516 else:
526 else:
517 base_idx = 0
527 base_idx = 0
518 chunks = []
528 chunks = []
519 for tn in _walk_trie(root):
529 for tn in _walk_trie(root):
520 if tn.ondisk_id is not None:
530 if tn.ondisk_id is not None:
521 block_map[id(tn)] = tn.ondisk_id
531 block_map[id(tn)] = tn.ondisk_id
522 else:
532 else:
523 block_map[id(tn)] = len(chunks) + base_idx
533 block_map[id(tn)] = len(chunks) + base_idx
524 chunks.append(_persist_block(tn, block_map))
534 chunks.append(_persist_block(tn, block_map))
525 return b''.join(chunks)
535 return b''.join(chunks)
526
536
527
537
528 def _walk_trie(block):
538 def _walk_trie(block):
529 """yield all the block in a trie
539 """yield all the block in a trie
530
540
531 Children blocks are always yield before their parent block.
541 Children blocks are always yield before their parent block.
532 """
542 """
533 for (__, item) in sorted(block.items()):
543 for (__, item) in sorted(block.items()):
534 if isinstance(item, dict):
544 if isinstance(item, dict):
535 for sub_block in _walk_trie(item):
545 for sub_block in _walk_trie(item):
536 yield sub_block
546 yield sub_block
537 yield block
547 yield block
538
548
539
549
540 def _persist_block(block_node, block_map):
550 def _persist_block(block_node, block_map):
541 """produce persistent binary data for a single block
551 """produce persistent binary data for a single block
542
552
543 Children block are assumed to be already persisted and present in
553 Children block are assumed to be already persisted and present in
544 block_map.
554 block_map.
545 """
555 """
546 data = tuple(_to_value(v, block_map) for v in block_node)
556 data = tuple(_to_value(v, block_map) for v in block_node)
547 return S_BLOCK.pack(*data)
557 return S_BLOCK.pack(*data)
548
558
549
559
550 def _to_value(item, block_map):
560 def _to_value(item, block_map):
551 """persist any value as an integer"""
561 """persist any value as an integer"""
552 if item is None:
562 if item is None:
553 return NO_ENTRY
563 return NO_ENTRY
554 elif isinstance(item, dict):
564 elif isinstance(item, dict):
555 return block_map[id(item)]
565 return block_map[id(item)]
556 else:
566 else:
557 return _transform_rev(item)
567 return _transform_rev(item)
558
568
559
569
560 def parse_data(data):
570 def parse_data(data):
561 """parse parse nodemap data into a nodemap Trie"""
571 """parse parse nodemap data into a nodemap Trie"""
562 if (len(data) % S_BLOCK.size) != 0:
572 if (len(data) % S_BLOCK.size) != 0:
563 msg = b"nodemap data size is not a multiple of block size (%d): %d"
573 msg = b"nodemap data size is not a multiple of block size (%d): %d"
564 raise error.Abort(msg % (S_BLOCK.size, len(data)))
574 raise error.Abort(msg % (S_BLOCK.size, len(data)))
565 if not data:
575 if not data:
566 return Block(), None
576 return Block(), None
567 block_map = {}
577 block_map = {}
568 new_blocks = []
578 new_blocks = []
569 for i in range(0, len(data), S_BLOCK.size):
579 for i in range(0, len(data), S_BLOCK.size):
570 block = Block()
580 block = Block()
571 block.ondisk_id = len(block_map)
581 block.ondisk_id = len(block_map)
572 block_map[block.ondisk_id] = block
582 block_map[block.ondisk_id] = block
573 block_data = data[i : i + S_BLOCK.size]
583 block_data = data[i : i + S_BLOCK.size]
574 values = S_BLOCK.unpack(block_data)
584 values = S_BLOCK.unpack(block_data)
575 new_blocks.append((block, values))
585 new_blocks.append((block, values))
576 for b, values in new_blocks:
586 for b, values in new_blocks:
577 for idx, v in enumerate(values):
587 for idx, v in enumerate(values):
578 if v == NO_ENTRY:
588 if v == NO_ENTRY:
579 continue
589 continue
580 elif v >= 0:
590 elif v >= 0:
581 b[idx] = block_map[v]
591 b[idx] = block_map[v]
582 else:
592 else:
583 b[idx] = _transform_rev(v)
593 b[idx] = _transform_rev(v)
584 return block, i // S_BLOCK.size
594 return block, i // S_BLOCK.size
585
595
586
596
587 # debug utility
597 # debug utility
588
598
589
599
590 def check_data(ui, index, data):
600 def check_data(ui, index, data):
591 """verify that the provided nodemap data are valid for the given idex"""
601 """verify that the provided nodemap data are valid for the given idex"""
592 ret = 0
602 ret = 0
593 ui.status((b"revision in index: %d\n") % len(index))
603 ui.status((b"revision in index: %d\n") % len(index))
594 root, __ = parse_data(data)
604 root, __ = parse_data(data)
595 all_revs = set(_all_revisions(root))
605 all_revs = set(_all_revisions(root))
596 ui.status((b"revision in nodemap: %d\n") % len(all_revs))
606 ui.status((b"revision in nodemap: %d\n") % len(all_revs))
597 for r in range(len(index)):
607 for r in range(len(index)):
598 if r not in all_revs:
608 if r not in all_revs:
599 msg = b" revision missing from nodemap: %d\n" % r
609 msg = b" revision missing from nodemap: %d\n" % r
600 ui.write_err(msg)
610 ui.write_err(msg)
601 ret = 1
611 ret = 1
602 else:
612 else:
603 all_revs.remove(r)
613 all_revs.remove(r)
604 nm_rev = _find_node(root, hex(index[r][7]))
614 nm_rev = _find_node(root, hex(index[r][7]))
605 if nm_rev is None:
615 if nm_rev is None:
606 msg = b" revision node does not match any entries: %d\n" % r
616 msg = b" revision node does not match any entries: %d\n" % r
607 ui.write_err(msg)
617 ui.write_err(msg)
608 ret = 1
618 ret = 1
609 elif nm_rev != r:
619 elif nm_rev != r:
610 msg = (
620 msg = (
611 b" revision node does not match the expected revision: "
621 b" revision node does not match the expected revision: "
612 b"%d != %d\n" % (r, nm_rev)
622 b"%d != %d\n" % (r, nm_rev)
613 )
623 )
614 ui.write_err(msg)
624 ui.write_err(msg)
615 ret = 1
625 ret = 1
616
626
617 if all_revs:
627 if all_revs:
618 for r in sorted(all_revs):
628 for r in sorted(all_revs):
619 msg = b" extra revision in nodemap: %d\n" % r
629 msg = b" extra revision in nodemap: %d\n" % r
620 ui.write_err(msg)
630 ui.write_err(msg)
621 ret = 1
631 ret = 1
622 return ret
632 return ret
623
633
624
634
625 def _all_revisions(root):
635 def _all_revisions(root):
626 """return all revisions stored in a Trie"""
636 """return all revisions stored in a Trie"""
627 for block in _walk_trie(root):
637 for block in _walk_trie(root):
628 for v in block:
638 for v in block:
629 if v is None or isinstance(v, Block):
639 if v is None or isinstance(v, Block):
630 continue
640 continue
631 yield v
641 yield v
632
642
633
643
634 def _find_node(block, node):
644 def _find_node(block, node):
635 """find the revision associated with a given node"""
645 """find the revision associated with a given node"""
636 entry = block.get(_to_int(node[0:1]))
646 entry = block.get(_to_int(node[0:1]))
637 if isinstance(entry, dict):
647 if isinstance(entry, dict):
638 return _find_node(entry, node[1:])
648 return _find_node(entry, node[1:])
639 return entry
649 return entry
640
650
641
651
642 def get_nodemap_file(revlog):
652 def get_nodemap_file(revlog):
643 if revlog._trypending:
653 if revlog._trypending:
644 pending_path = revlog.radix + b".n.a"
654 pending_path = revlog.radix + b".n.a"
645 if revlog.opener.exists(pending_path):
655 if revlog.opener.exists(pending_path):
646 return pending_path
656 return pending_path
647 return revlog.radix + b".n"
657 return revlog.radix + b".n"
@@ -1,1099 +1,1297 b''
1 ===================================
1 ===================================
2 Test the persistent on-disk nodemap
2 Test the persistent on-disk nodemap
3 ===================================
3 ===================================
4
4
5
5
6 $ cat << EOF >> $HGRCPATH
6 $ cat << EOF >> $HGRCPATH
7 > [format]
7 > [format]
8 > use-share-safe=yes
8 > use-share-safe=yes
9 > [extensions]
9 > [extensions]
10 > share=
10 > share=
11 > EOF
11 > EOF
12
12
13 #if no-rust
13 #if no-rust
14
14
15 $ cat << EOF >> $HGRCPATH
15 $ cat << EOF >> $HGRCPATH
16 > [format]
16 > [format]
17 > use-persistent-nodemap=yes
17 > use-persistent-nodemap=yes
18 > [devel]
18 > [devel]
19 > persistent-nodemap=yes
19 > persistent-nodemap=yes
20 > EOF
20 > EOF
21
21
22 #endif
22 #endif
23
23
24 $ hg init test-repo --config storage.revlog.persistent-nodemap.slow-path=allow
24 $ hg init test-repo --config storage.revlog.persistent-nodemap.slow-path=allow
25 $ cd test-repo
25 $ cd test-repo
26
26
27 Check handling of the default slow-path value
27 Check handling of the default slow-path value
28
28
29 #if no-pure no-rust
29 #if no-pure no-rust
30
30
31 $ hg id
31 $ hg id
32 abort: accessing `persistent-nodemap` repository without associated fast implementation.
32 abort: accessing `persistent-nodemap` repository without associated fast implementation.
33 (check `hg help config.format.use-persistent-nodemap` for details)
33 (check `hg help config.format.use-persistent-nodemap` for details)
34 [255]
34 [255]
35
35
36 Unlock further check (we are here to test the feature)
36 Unlock further check (we are here to test the feature)
37
37
38 $ cat << EOF >> $HGRCPATH
38 $ cat << EOF >> $HGRCPATH
39 > [storage]
39 > [storage]
40 > # to avoid spamming the test
40 > # to avoid spamming the test
41 > revlog.persistent-nodemap.slow-path=allow
41 > revlog.persistent-nodemap.slow-path=allow
42 > EOF
42 > EOF
43
43
44 #endif
44 #endif
45
45
46 #if rust
46 #if rust
47
47
48 Regression test for a previous bug in Rust/C FFI for the `Revlog_CAPI` capsule:
48 Regression test for a previous bug in Rust/C FFI for the `Revlog_CAPI` capsule:
49 in places where `mercurial/cext/revlog.c` function signatures use `Py_ssize_t`
49 in places where `mercurial/cext/revlog.c` function signatures use `Py_ssize_t`
50 (64 bits on Linux x86_64), corresponding declarations in `rust/hg-cpython/src/cindex.rs`
50 (64 bits on Linux x86_64), corresponding declarations in `rust/hg-cpython/src/cindex.rs`
51 incorrectly used `libc::c_int` (32 bits).
51 incorrectly used `libc::c_int` (32 bits).
52 As a result, -1 passed from Rust for the null revision became 4294967295 in C.
52 As a result, -1 passed from Rust for the null revision became 4294967295 in C.
53
53
54 $ hg log -r 00000000
54 $ hg log -r 00000000
55 changeset: -1:000000000000
55 changeset: -1:000000000000
56 tag: tip
56 tag: tip
57 user:
57 user:
58 date: Thu Jan 01 00:00:00 1970 +0000
58 date: Thu Jan 01 00:00:00 1970 +0000
59
59
60
60
61 #endif
61 #endif
62
62
63
63
64 $ hg debugformat
64 $ hg debugformat
65 format-variant repo
65 format-variant repo
66 fncache: yes
66 fncache: yes
67 dirstate-v2: no
67 dirstate-v2: no
68 dotencode: yes
68 dotencode: yes
69 generaldelta: yes
69 generaldelta: yes
70 share-safe: yes
70 share-safe: yes
71 sparserevlog: yes
71 sparserevlog: yes
72 persistent-nodemap: yes
72 persistent-nodemap: yes
73 copies-sdc: no
73 copies-sdc: no
74 revlog-v2: no
74 revlog-v2: no
75 changelog-v2: no
75 changelog-v2: no
76 plain-cl-delta: yes
76 plain-cl-delta: yes
77 compression: zlib (no-zstd !)
77 compression: zlib (no-zstd !)
78 compression: zstd (zstd !)
78 compression: zstd (zstd !)
79 compression-level: default
79 compression-level: default
80 $ hg debugbuilddag .+5000 --new-file
80 $ hg debugbuilddag .+5000 --new-file
81
81
82 $ hg debugnodemap --metadata
82 $ hg debugnodemap --metadata
83 uid: ???????? (glob)
83 uid: ???????? (glob)
84 tip-rev: 5000
84 tip-rev: 5000
85 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
85 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
86 data-length: 121088
86 data-length: 121088
87 data-unused: 0
87 data-unused: 0
88 data-unused: 0.000%
88 data-unused: 0.000%
89 $ f --size .hg/store/00changelog.n
89 $ f --size .hg/store/00changelog.n
90 .hg/store/00changelog.n: size=62
90 .hg/store/00changelog.n: size=62
91
91
92 Simple lookup works
92 Simple lookup works
93
93
94 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
94 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
95 $ hg log -r "$ANYNODE" --template '{rev}\n'
95 $ hg log -r "$ANYNODE" --template '{rev}\n'
96 5000
96 5000
97
97
98
98
99 #if rust
99 #if rust
100
100
101 $ f --sha256 .hg/store/00changelog-*.nd
101 $ f --sha256 .hg/store/00changelog-*.nd
102 .hg/store/00changelog-????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
102 .hg/store/00changelog-????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
103
103
104 $ f --sha256 .hg/store/00manifest-*.nd
104 $ f --sha256 .hg/store/00manifest-*.nd
105 .hg/store/00manifest-????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
105 .hg/store/00manifest-????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
106 $ hg debugnodemap --dump-new | f --sha256 --size
106 $ hg debugnodemap --dump-new | f --sha256 --size
107 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
107 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
108 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
108 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
109 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
109 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
110 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
110 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
111 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
111 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
112 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
112 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
113 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
113 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
114 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
114 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
115 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
115 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
116 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
116 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
117 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
117 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
118 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
118 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
119 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
119 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
120 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
120 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
121 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
121 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
122 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
122 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
123 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
123 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
124 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
124 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
125 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
125 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
126
126
127
127
128 #else
128 #else
129
129
130 $ f --sha256 .hg/store/00changelog-*.nd
130 $ f --sha256 .hg/store/00changelog-*.nd
131 .hg/store/00changelog-????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
131 .hg/store/00changelog-????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
132 $ hg debugnodemap --dump-new | f --sha256 --size
132 $ hg debugnodemap --dump-new | f --sha256 --size
133 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
133 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
134 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
134 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
135 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
135 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
136 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
136 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
137 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
137 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
138 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
138 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
139 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
139 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
140 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
140 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
141 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
141 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
142 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
142 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
143 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
143 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
144 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
144 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
145 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
145 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
146 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
146 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
147 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
147 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
148 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
148 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
149 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
149 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
150 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
150 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
151 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
151 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
152
152
153 #endif
153 #endif
154
154
155 $ hg debugnodemap --check
155 $ hg debugnodemap --check
156 revision in index: 5001
156 revision in index: 5001
157 revision in nodemap: 5001
157 revision in nodemap: 5001
158
158
159 add a new commit
159 add a new commit
160
160
161 $ hg up
161 $ hg up
162 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
162 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 $ echo foo > foo
163 $ echo foo > foo
164 $ hg add foo
164 $ hg add foo
165
165
166
166
167 Check slow-path config value handling
167 Check slow-path config value handling
168 -------------------------------------
168 -------------------------------------
169
169
170 #if no-pure no-rust
170 #if no-pure no-rust
171
171
172 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
172 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
173 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
173 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
174 falling back to default value: abort
174 falling back to default value: abort
175 abort: accessing `persistent-nodemap` repository without associated fast implementation.
175 abort: accessing `persistent-nodemap` repository without associated fast implementation.
176 (check `hg help config.format.use-persistent-nodemap` for details)
176 (check `hg help config.format.use-persistent-nodemap` for details)
177 [255]
177 [255]
178
178
179 $ hg log -r . --config "storage.revlog.persistent-nodemap.slow-path=warn"
179 $ hg log -r . --config "storage.revlog.persistent-nodemap.slow-path=warn"
180 warning: accessing `persistent-nodemap` repository without associated fast implementation.
180 warning: accessing `persistent-nodemap` repository without associated fast implementation.
181 (check `hg help config.format.use-persistent-nodemap` for details)
181 (check `hg help config.format.use-persistent-nodemap` for details)
182 changeset: 5000:6b02b8c7b966
182 changeset: 5000:6b02b8c7b966
183 tag: tip
183 tag: tip
184 user: debugbuilddag
184 user: debugbuilddag
185 date: Thu Jan 01 01:23:20 1970 +0000
185 date: Thu Jan 01 01:23:20 1970 +0000
186 summary: r5000
186 summary: r5000
187
187
188 $ hg ci -m 'foo' --config "storage.revlog.persistent-nodemap.slow-path=abort"
188 $ hg ci -m 'foo' --config "storage.revlog.persistent-nodemap.slow-path=abort"
189 abort: accessing `persistent-nodemap` repository without associated fast implementation.
189 abort: accessing `persistent-nodemap` repository without associated fast implementation.
190 (check `hg help config.format.use-persistent-nodemap` for details)
190 (check `hg help config.format.use-persistent-nodemap` for details)
191 [255]
191 [255]
192
192
193 #else
193 #else
194
194
195 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
195 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
196 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
196 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
197 falling back to default value: abort
197 falling back to default value: abort
198 6b02b8c7b966+ tip
198 6b02b8c7b966+ tip
199
199
200 #endif
200 #endif
201
201
202 $ hg ci -m 'foo'
202 $ hg ci -m 'foo'
203
203
204 #if no-pure no-rust
204 #if no-pure no-rust
205 $ hg debugnodemap --metadata
205 $ hg debugnodemap --metadata
206 uid: ???????? (glob)
206 uid: ???????? (glob)
207 tip-rev: 5001
207 tip-rev: 5001
208 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
208 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
209 data-length: 121088
209 data-length: 121088
210 data-unused: 0
210 data-unused: 0
211 data-unused: 0.000%
211 data-unused: 0.000%
212 #else
212 #else
213 $ hg debugnodemap --metadata
213 $ hg debugnodemap --metadata
214 uid: ???????? (glob)
214 uid: ???????? (glob)
215 tip-rev: 5001
215 tip-rev: 5001
216 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
216 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
217 data-length: 121344
217 data-length: 121344
218 data-unused: 256
218 data-unused: 256
219 data-unused: 0.211%
219 data-unused: 0.211%
220 #endif
220 #endif
221
221
222 $ f --size .hg/store/00changelog.n
222 $ f --size .hg/store/00changelog.n
223 .hg/store/00changelog.n: size=62
223 .hg/store/00changelog.n: size=62
224
224
225 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
225 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
226
226
227 #if pure
227 #if pure
228 $ f --sha256 .hg/store/00changelog-*.nd --size
228 $ f --sha256 .hg/store/00changelog-*.nd --size
229 .hg/store/00changelog-????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
229 .hg/store/00changelog-????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
230 #endif
230 #endif
231
231
232 #if rust
232 #if rust
233 $ f --sha256 .hg/store/00changelog-*.nd --size
233 $ f --sha256 .hg/store/00changelog-*.nd --size
234 .hg/store/00changelog-????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
234 .hg/store/00changelog-????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
235 #endif
235 #endif
236
236
237 #if no-pure no-rust
237 #if no-pure no-rust
238 $ f --sha256 .hg/store/00changelog-*.nd --size
238 $ f --sha256 .hg/store/00changelog-*.nd --size
239 .hg/store/00changelog-????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
239 .hg/store/00changelog-????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
240 #endif
240 #endif
241
241
242 $ hg debugnodemap --check
242 $ hg debugnodemap --check
243 revision in index: 5002
243 revision in index: 5002
244 revision in nodemap: 5002
244 revision in nodemap: 5002
245
245
246 Test code path without mmap
246 Test code path without mmap
247 ---------------------------
247 ---------------------------
248
248
249 $ echo bar > bar
249 $ echo bar > bar
250 $ hg add bar
250 $ hg add bar
251 $ hg ci -m 'bar' --config storage.revlog.persistent-nodemap.mmap=no
251 $ hg ci -m 'bar' --config storage.revlog.persistent-nodemap.mmap=no
252
252
253 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=yes
253 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=yes
254 revision in index: 5003
254 revision in index: 5003
255 revision in nodemap: 5003
255 revision in nodemap: 5003
256 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=no
256 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=no
257 revision in index: 5003
257 revision in index: 5003
258 revision in nodemap: 5003
258 revision in nodemap: 5003
259
259
260
260
261 #if pure
261 #if pure
262 $ hg debugnodemap --metadata
262 $ hg debugnodemap --metadata
263 uid: ???????? (glob)
263 uid: ???????? (glob)
264 tip-rev: 5002
264 tip-rev: 5002
265 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
265 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
266 data-length: 121600
266 data-length: 121600
267 data-unused: 512
267 data-unused: 512
268 data-unused: 0.421%
268 data-unused: 0.421%
269 $ f --sha256 .hg/store/00changelog-*.nd --size
269 $ f --sha256 .hg/store/00changelog-*.nd --size
270 .hg/store/00changelog-????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
270 .hg/store/00changelog-????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
271 #endif
271 #endif
272 #if rust
272 #if rust
273 $ hg debugnodemap --metadata
273 $ hg debugnodemap --metadata
274 uid: ???????? (glob)
274 uid: ???????? (glob)
275 tip-rev: 5002
275 tip-rev: 5002
276 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
276 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
277 data-length: 121600
277 data-length: 121600
278 data-unused: 512
278 data-unused: 512
279 data-unused: 0.421%
279 data-unused: 0.421%
280 $ f --sha256 .hg/store/00changelog-*.nd --size
280 $ f --sha256 .hg/store/00changelog-*.nd --size
281 .hg/store/00changelog-????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
281 .hg/store/00changelog-????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
282 #endif
282 #endif
283 #if no-pure no-rust
283 #if no-pure no-rust
284 $ hg debugnodemap --metadata
284 $ hg debugnodemap --metadata
285 uid: ???????? (glob)
285 uid: ???????? (glob)
286 tip-rev: 5002
286 tip-rev: 5002
287 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
287 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
288 data-length: 121088
288 data-length: 121088
289 data-unused: 0
289 data-unused: 0
290 data-unused: 0.000%
290 data-unused: 0.000%
291 $ f --sha256 .hg/store/00changelog-*.nd --size
291 $ f --sha256 .hg/store/00changelog-*.nd --size
292 .hg/store/00changelog-????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
292 .hg/store/00changelog-????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
293 #endif
293 #endif
294
294
295 Test force warming the cache
295 Test force warming the cache
296
296
297 $ rm .hg/store/00changelog.n
297 $ rm .hg/store/00changelog.n
298 $ hg debugnodemap --metadata
298 $ hg debugnodemap --metadata
299 $ hg debugupdatecache
299 $ hg debugupdatecache
300 #if pure
300 #if pure
301 $ hg debugnodemap --metadata
301 $ hg debugnodemap --metadata
302 uid: ???????? (glob)
302 uid: ???????? (glob)
303 tip-rev: 5002
303 tip-rev: 5002
304 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
304 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
305 data-length: 121088
305 data-length: 121088
306 data-unused: 0
306 data-unused: 0
307 data-unused: 0.000%
307 data-unused: 0.000%
308 #else
308 #else
309 $ hg debugnodemap --metadata
309 $ hg debugnodemap --metadata
310 uid: ???????? (glob)
310 uid: ???????? (glob)
311 tip-rev: 5002
311 tip-rev: 5002
312 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
312 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
313 data-length: 121088
313 data-length: 121088
314 data-unused: 0
314 data-unused: 0
315 data-unused: 0.000%
315 data-unused: 0.000%
316 #endif
316 #endif
317
317
318 Check out of sync nodemap
318 Check out of sync nodemap
319 =========================
319 =========================
320
320
321 First copy old data on the side.
321 First copy old data on the side.
322
322
323 $ mkdir ../tmp-copies
323 $ mkdir ../tmp-copies
324 $ cp .hg/store/00changelog-????????.nd .hg/store/00changelog.n ../tmp-copies
324 $ cp .hg/store/00changelog-????????.nd .hg/store/00changelog.n ../tmp-copies
325
325
326 Nodemap lagging behind
326 Nodemap lagging behind
327 ----------------------
327 ----------------------
328
328
329 make a new commit
329 make a new commit
330
330
331 $ echo bar2 > bar
331 $ echo bar2 > bar
332 $ hg ci -m 'bar2'
332 $ hg ci -m 'bar2'
333 $ NODE=`hg log -r tip -T '{node}\n'`
333 $ NODE=`hg log -r tip -T '{node}\n'`
334 $ hg log -r "$NODE" -T '{rev}\n'
334 $ hg log -r "$NODE" -T '{rev}\n'
335 5003
335 5003
336
336
337 If the nodemap is lagging behind, it can catch up fine
337 If the nodemap is lagging behind, it can catch up fine
338
338
339 $ hg debugnodemap --metadata
339 $ hg debugnodemap --metadata
340 uid: ???????? (glob)
340 uid: ???????? (glob)
341 tip-rev: 5003
341 tip-rev: 5003
342 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
342 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
343 data-length: 121344 (pure !)
343 data-length: 121344 (pure !)
344 data-length: 121344 (rust !)
344 data-length: 121344 (rust !)
345 data-length: 121152 (no-rust no-pure !)
345 data-length: 121152 (no-rust no-pure !)
346 data-unused: 192 (pure !)
346 data-unused: 192 (pure !)
347 data-unused: 192 (rust !)
347 data-unused: 192 (rust !)
348 data-unused: 0 (no-rust no-pure !)
348 data-unused: 0 (no-rust no-pure !)
349 data-unused: 0.158% (pure !)
349 data-unused: 0.158% (pure !)
350 data-unused: 0.158% (rust !)
350 data-unused: 0.158% (rust !)
351 data-unused: 0.000% (no-rust no-pure !)
351 data-unused: 0.000% (no-rust no-pure !)
352 $ cp -f ../tmp-copies/* .hg/store/
352 $ cp -f ../tmp-copies/* .hg/store/
353 $ hg debugnodemap --metadata
353 $ hg debugnodemap --metadata
354 uid: ???????? (glob)
354 uid: ???????? (glob)
355 tip-rev: 5002
355 tip-rev: 5002
356 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
356 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
357 data-length: 121088
357 data-length: 121088
358 data-unused: 0
358 data-unused: 0
359 data-unused: 0.000%
359 data-unused: 0.000%
360 $ hg log -r "$NODE" -T '{rev}\n'
360 $ hg log -r "$NODE" -T '{rev}\n'
361 5003
361 5003
362
362
363 changelog altered
363 changelog altered
364 -----------------
364 -----------------
365
365
366 If the nodemap is not gated behind a requirements, an unaware client can alter
366 If the nodemap is not gated behind a requirements, an unaware client can alter
367 the repository so the revlog used to generate the nodemap is not longer
367 the repository so the revlog used to generate the nodemap is not longer
368 compatible with the persistent nodemap. We need to detect that.
368 compatible with the persistent nodemap. We need to detect that.
369
369
370 $ hg up "$NODE~5"
370 $ hg up "$NODE~5"
371 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
371 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
372 $ echo bar > babar
372 $ echo bar > babar
373 $ hg add babar
373 $ hg add babar
374 $ hg ci -m 'babar'
374 $ hg ci -m 'babar'
375 created new head
375 created new head
376 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
376 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
377 $ hg log -r "$OTHERNODE" -T '{rev}\n'
377 $ hg log -r "$OTHERNODE" -T '{rev}\n'
378 5004
378 5004
379
379
380 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
380 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
381
381
382 the nodemap should detect the changelog have been tampered with and recover.
382 the nodemap should detect the changelog have been tampered with and recover.
383
383
384 $ hg debugnodemap --metadata
384 $ hg debugnodemap --metadata
385 uid: ???????? (glob)
385 uid: ???????? (glob)
386 tip-rev: 5002
386 tip-rev: 5002
387 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
387 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
388 data-length: 121536 (pure !)
388 data-length: 121536 (pure !)
389 data-length: 121088 (rust !)
389 data-length: 121088 (rust !)
390 data-length: 121088 (no-pure no-rust !)
390 data-length: 121088 (no-pure no-rust !)
391 data-unused: 448 (pure !)
391 data-unused: 448 (pure !)
392 data-unused: 0 (rust !)
392 data-unused: 0 (rust !)
393 data-unused: 0 (no-pure no-rust !)
393 data-unused: 0 (no-pure no-rust !)
394 data-unused: 0.000% (rust !)
394 data-unused: 0.000% (rust !)
395 data-unused: 0.369% (pure !)
395 data-unused: 0.369% (pure !)
396 data-unused: 0.000% (no-pure no-rust !)
396 data-unused: 0.000% (no-pure no-rust !)
397
397
398 $ cp -f ../tmp-copies/* .hg/store/
398 $ cp -f ../tmp-copies/* .hg/store/
399 $ hg debugnodemap --metadata
399 $ hg debugnodemap --metadata
400 uid: ???????? (glob)
400 uid: ???????? (glob)
401 tip-rev: 5002
401 tip-rev: 5002
402 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
402 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
403 data-length: 121088
403 data-length: 121088
404 data-unused: 0
404 data-unused: 0
405 data-unused: 0.000%
405 data-unused: 0.000%
406 $ hg log -r "$OTHERNODE" -T '{rev}\n'
406 $ hg log -r "$OTHERNODE" -T '{rev}\n'
407 5002
407 5002
408
408
409 missing data file
409 missing data file
410 -----------------
410 -----------------
411
411
412 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
412 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
413 > sed 's/uid: //'`
413 > sed 's/uid: //'`
414 $ FILE=.hg/store/00changelog-"${UUID}".nd
414 $ FILE=.hg/store/00changelog-"${UUID}".nd
415 $ mv $FILE ../tmp-data-file
415 $ mv $FILE ../tmp-data-file
416 $ cp .hg/store/00changelog.n ../tmp-docket
416 $ cp .hg/store/00changelog.n ../tmp-docket
417
417
418 mercurial don't crash
418 mercurial don't crash
419
419
420 $ hg log -r .
420 $ hg log -r .
421 changeset: 5002:b355ef8adce0
421 changeset: 5002:b355ef8adce0
422 tag: tip
422 tag: tip
423 parent: 4998:d918ad6d18d3
423 parent: 4998:d918ad6d18d3
424 user: test
424 user: test
425 date: Thu Jan 01 00:00:00 1970 +0000
425 date: Thu Jan 01 00:00:00 1970 +0000
426 summary: babar
426 summary: babar
427
427
428 $ hg debugnodemap --metadata
428 $ hg debugnodemap --metadata
429
429
430 $ hg debugupdatecache
430 $ hg debugupdatecache
431 $ hg debugnodemap --metadata
431 $ hg debugnodemap --metadata
432 uid: * (glob)
432 uid: * (glob)
433 tip-rev: 5002
433 tip-rev: 5002
434 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
434 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
435 data-length: 121088
435 data-length: 121088
436 data-unused: 0
436 data-unused: 0
437 data-unused: 0.000%
437 data-unused: 0.000%
438
438
439 Sub-case: fallback for corrupted data file
439 Sub-case: fallback for corrupted data file
440 ------------------------------------------
440 ------------------------------------------
441
441
442 Sabotaging the data file so that nodemap resolutions fail, triggering fallback to
442 Sabotaging the data file so that nodemap resolutions fail, triggering fallback to
443 (non-persistent) C implementation.
443 (non-persistent) C implementation.
444
444
445
445
446 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
446 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
447 > sed 's/uid: //'`
447 > sed 's/uid: //'`
448 $ FILE=.hg/store/00changelog-"${UUID}".nd
448 $ FILE=.hg/store/00changelog-"${UUID}".nd
449 $ python -c "fobj = open('$FILE', 'r+b'); fobj.write(b'\xff' * 121088); fobj.close()"
449 $ python -c "fobj = open('$FILE', 'r+b'); fobj.write(b'\xff' * 121088); fobj.close()"
450
450
451 The nodemap data file is still considered in sync with the docket. This
451 The nodemap data file is still considered in sync with the docket. This
452 would fail without the fallback to the (non-persistent) C implementation:
452 would fail without the fallback to the (non-persistent) C implementation:
453
453
454 $ hg log -r b355ef8adce0949b8bdf6afc72ca853740d65944 -T '{rev}\n' --traceback
454 $ hg log -r b355ef8adce0949b8bdf6afc72ca853740d65944 -T '{rev}\n' --traceback
455 5002
455 5002
456
456
457 The nodemap data file hasn't been fixed, more tests can be inserted:
457 The nodemap data file hasn't been fixed, more tests can be inserted:
458
458
459 $ hg debugnodemap --dump-disk | f --bytes=256 --hexdump --size
459 $ hg debugnodemap --dump-disk | f --bytes=256 --hexdump --size
460 size=121088
460 size=121088
461 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
461 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
462 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
462 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
463 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
463 0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
464 0030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
464 0030: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
465 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
465 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
466 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
466 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
467 0060: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
467 0060: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
468 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
468 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
469 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
469 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
470 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
470 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
471 00a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
471 00a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
472 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
472 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
473 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
473 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
474 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
474 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
475 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
475 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
476 00f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
476 00f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
477
477
478 $ mv ../tmp-data-file $FILE
478 $ mv ../tmp-data-file $FILE
479 $ mv ../tmp-docket .hg/store/00changelog.n
479 $ mv ../tmp-docket .hg/store/00changelog.n
480
480
481 Check transaction related property
481 Check transaction related property
482 ==================================
482 ==================================
483
483
484 An up to date nodemap should be available to shell hooks,
484 An up to date nodemap should be available to shell hooks,
485
485
486 $ echo dsljfl > a
486 $ echo dsljfl > a
487 $ hg add a
487 $ hg add a
488 $ hg ci -m a
488 $ hg ci -m a
489 $ hg debugnodemap --metadata
489 $ hg debugnodemap --metadata
490 uid: ???????? (glob)
490 uid: ???????? (glob)
491 tip-rev: 5003
491 tip-rev: 5003
492 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
492 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
493 data-length: 121088
493 data-length: 121088
494 data-unused: 0
494 data-unused: 0
495 data-unused: 0.000%
495 data-unused: 0.000%
496 $ echo babar2 > babar
496 $ echo babar2 > babar
497 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
497 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
498 uid: ???????? (glob)
498 uid: ???????? (glob)
499 tip-rev: 5004
499 tip-rev: 5004
500 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
500 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
501 data-length: 121280 (pure !)
501 data-length: 121280 (pure !)
502 data-length: 121280 (rust !)
502 data-length: 121280 (rust !)
503 data-length: 121088 (no-pure no-rust !)
503 data-length: 121088 (no-pure no-rust !)
504 data-unused: 192 (pure !)
504 data-unused: 192 (pure !)
505 data-unused: 192 (rust !)
505 data-unused: 192 (rust !)
506 data-unused: 0 (no-pure no-rust !)
506 data-unused: 0 (no-pure no-rust !)
507 data-unused: 0.158% (pure !)
507 data-unused: 0.158% (pure !)
508 data-unused: 0.158% (rust !)
508 data-unused: 0.158% (rust !)
509 data-unused: 0.000% (no-pure no-rust !)
509 data-unused: 0.000% (no-pure no-rust !)
510 $ hg debugnodemap --metadata
510 $ hg debugnodemap --metadata
511 uid: ???????? (glob)
511 uid: ???????? (glob)
512 tip-rev: 5004
512 tip-rev: 5004
513 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
513 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
514 data-length: 121280 (pure !)
514 data-length: 121280 (pure !)
515 data-length: 121280 (rust !)
515 data-length: 121280 (rust !)
516 data-length: 121088 (no-pure no-rust !)
516 data-length: 121088 (no-pure no-rust !)
517 data-unused: 192 (pure !)
517 data-unused: 192 (pure !)
518 data-unused: 192 (rust !)
518 data-unused: 192 (rust !)
519 data-unused: 0 (no-pure no-rust !)
519 data-unused: 0 (no-pure no-rust !)
520 data-unused: 0.158% (pure !)
520 data-unused: 0.158% (pure !)
521 data-unused: 0.158% (rust !)
521 data-unused: 0.158% (rust !)
522 data-unused: 0.000% (no-pure no-rust !)
522 data-unused: 0.000% (no-pure no-rust !)
523
523
524 Another process does not see the pending nodemap content during run.
524 Another process does not see the pending nodemap content during run.
525
525
526 $ echo qpoasp > a
526 $ echo qpoasp > a
527 $ hg ci -m a2 \
527 $ hg ci -m a2 \
528 > --config "hooks.pretxnclose=sh \"$RUNTESTDIR/testlib/wait-on-file\" 20 sync-repo-read sync-txn-pending" \
528 > --config "hooks.pretxnclose=sh \"$RUNTESTDIR/testlib/wait-on-file\" 20 sync-repo-read sync-txn-pending" \
529 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
529 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
530
530
531 (read the repository while the commit transaction is pending)
531 (read the repository while the commit transaction is pending)
532
532
533 $ sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-pending && \
533 $ sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-pending && \
534 > hg debugnodemap --metadata && \
534 > hg debugnodemap --metadata && \
535 > sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-close sync-repo-read
535 > sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-close sync-repo-read
536 uid: ???????? (glob)
536 uid: ???????? (glob)
537 tip-rev: 5004
537 tip-rev: 5004
538 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
538 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
539 data-length: 121280 (pure !)
539 data-length: 121280 (pure !)
540 data-length: 121280 (rust !)
540 data-length: 121280 (rust !)
541 data-length: 121088 (no-pure no-rust !)
541 data-length: 121088 (no-pure no-rust !)
542 data-unused: 192 (pure !)
542 data-unused: 192 (pure !)
543 data-unused: 192 (rust !)
543 data-unused: 192 (rust !)
544 data-unused: 0 (no-pure no-rust !)
544 data-unused: 0 (no-pure no-rust !)
545 data-unused: 0.158% (pure !)
545 data-unused: 0.158% (pure !)
546 data-unused: 0.158% (rust !)
546 data-unused: 0.158% (rust !)
547 data-unused: 0.000% (no-pure no-rust !)
547 data-unused: 0.000% (no-pure no-rust !)
548 $ hg debugnodemap --metadata
548 $ hg debugnodemap --metadata
549 uid: ???????? (glob)
549 uid: ???????? (glob)
550 tip-rev: 5005
550 tip-rev: 5005
551 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
551 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
552 data-length: 121536 (pure !)
552 data-length: 121536 (pure !)
553 data-length: 121536 (rust !)
553 data-length: 121536 (rust !)
554 data-length: 121088 (no-pure no-rust !)
554 data-length: 121088 (no-pure no-rust !)
555 data-unused: 448 (pure !)
555 data-unused: 448 (pure !)
556 data-unused: 448 (rust !)
556 data-unused: 448 (rust !)
557 data-unused: 0 (no-pure no-rust !)
557 data-unused: 0 (no-pure no-rust !)
558 data-unused: 0.369% (pure !)
558 data-unused: 0.369% (pure !)
559 data-unused: 0.369% (rust !)
559 data-unused: 0.369% (rust !)
560 data-unused: 0.000% (no-pure no-rust !)
560 data-unused: 0.000% (no-pure no-rust !)
561
561
562 $ cat output.txt
562 $ cat output.txt
563
563
564 Check that a failing transaction will properly revert the data
564 Check that a failing transaction will properly revert the data
565
565
566 $ echo plakfe > a
566 $ echo plakfe > a
567 $ f --size --sha256 .hg/store/00changelog-*.nd
567 $ f --size --sha256 .hg/store/00changelog-*.nd
568 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
568 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
569 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
569 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
570 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
570 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
571 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
571 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
572 transaction abort!
572 transaction abort!
573 rollback completed
573 rollback completed
574 abort: This is a late abort
574 abort: This is a late abort
575 [255]
575 [255]
576 $ hg debugnodemap --metadata
576 $ hg debugnodemap --metadata
577 uid: ???????? (glob)
577 uid: ???????? (glob)
578 tip-rev: 5005
578 tip-rev: 5005
579 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
579 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
580 data-length: 121536 (pure !)
580 data-length: 121536 (pure !)
581 data-length: 121536 (rust !)
581 data-length: 121536 (rust !)
582 data-length: 121088 (no-pure no-rust !)
582 data-length: 121088 (no-pure no-rust !)
583 data-unused: 448 (pure !)
583 data-unused: 448 (pure !)
584 data-unused: 448 (rust !)
584 data-unused: 448 (rust !)
585 data-unused: 0 (no-pure no-rust !)
585 data-unused: 0 (no-pure no-rust !)
586 data-unused: 0.369% (pure !)
586 data-unused: 0.369% (pure !)
587 data-unused: 0.369% (rust !)
587 data-unused: 0.369% (rust !)
588 data-unused: 0.000% (no-pure no-rust !)
588 data-unused: 0.000% (no-pure no-rust !)
589 $ f --size --sha256 .hg/store/00changelog-*.nd
589 $ f --size --sha256 .hg/store/00changelog-*.nd
590 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
590 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
591 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
591 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
592 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
592 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
593
593
594 Check that removing content does not confuse the nodemap
594 Check that removing content does not confuse the nodemap
595 --------------------------------------------------------
595 --------------------------------------------------------
596
596
597 removing data with rollback
597 removing data with rollback
598
598
599 $ echo aso > a
599 $ echo aso > a
600 $ hg ci -m a4
600 $ hg ci -m a4
601 $ hg rollback
601 $ hg rollback
602 repository tip rolled back to revision 5005 (undo commit)
602 repository tip rolled back to revision 5005 (undo commit)
603 working directory now based on revision 5005
603 working directory now based on revision 5005
604 $ hg id -r .
604 $ hg id -r .
605 90d5d3ba2fc4 tip
605 90d5d3ba2fc4 tip
606
606
607 removing data with strip
607 removing data with strip
608
608
609 $ echo aso > a
609 $ echo aso > a
610 $ hg ci -m a4
610 $ hg ci -m a4
611 $ hg --config extensions.strip= strip -r . --no-backup
611 $ hg --config extensions.strip= strip -r . --no-backup
612 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
612 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
613 $ hg id -r . --traceback
613 $ hg id -r . --traceback
614 90d5d3ba2fc4 tip
614 90d5d3ba2fc4 tip
615
615
616 (be a good citizen and regenerate the nodemap)
617 $ hg debugupdatecaches
618 $ hg debugnodemap --metadata
619 uid: * (glob)
620 tip-rev: 5005
621 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
622 data-length: 121088
623 data-unused: 0
624 data-unused: 0.000%
625
626 Check race condition when multiple process write new data to the repository
627 ---------------------------------------------------------------------------
628
629 In this test, we check that two writers touching the repositories will not
630 overwrite each other data. This test is prompted by the existent of issue6554.
631 Where a writer ended up using and outdated docket to update the repository. See
632 the dedicated extension for details on the race windows and read/write schedule
633 necessary to end up in this situation: testlib/persistent-nodemap-race-ext.py
634
635 The issue was initially observed on a server with a high push trafic, but it
636 can be reproduced using a share and two commiting process which seems simpler.
637
638 The test is Rust only as the other implementation does not use the same
639 read/write patterns.
640
641 $ cd ..
642
643 #if rust
644
645 $ cp -R test-repo race-repo
646 $ hg share race-repo ./other-wc --config format.use-share-safe=yes
647 updating working directory
648 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
649 $ hg debugformat -R ./race-repo | egrep 'share-safe|persistent-nodemap'
650 share-safe: yes
651 persistent-nodemap: yes
652 $ hg debugformat -R ./other-wc/ | egrep 'share-safe|persistent-nodemap'
653 share-safe: yes
654 persistent-nodemap: yes
655 $ hg -R ./other-wc update 'min(head())'
656 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
657 $ hg -R ./race-repo debugnodemap --metadata
658 uid: 43c37dde
659 tip-rev: 5005
660 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
661 data-length: 121088
662 data-unused: 0
663 data-unused: 0.000%
664 $ hg -R ./race-repo log -G -r 'head()'
665 @ changeset: 5005:90d5d3ba2fc4
666 | tag: tip
667 ~ user: test
668 date: Thu Jan 01 00:00:00 1970 +0000
669 summary: a2
670
671 o changeset: 5001:16395c3cf7e2
672 | user: test
673 ~ date: Thu Jan 01 00:00:00 1970 +0000
674 summary: foo
675
676 $ hg -R ./other-wc log -G -r 'head()'
677 o changeset: 5005:90d5d3ba2fc4
678 | tag: tip
679 ~ user: test
680 date: Thu Jan 01 00:00:00 1970 +0000
681 summary: a2
682
683 @ changeset: 5001:16395c3cf7e2
684 | user: test
685 ~ date: Thu Jan 01 00:00:00 1970 +0000
686 summary: foo
687
688 $ echo left-side-race > race-repo/left-side-race
689 $ hg -R ./race-repo/ add race-repo/left-side-race
690
691 $ echo right-side-race > ./other-wc/right-side-race
692 $ hg -R ./other-wc/ add ./other-wc/right-side-race
693
694 $ mkdir sync-files
695 $ mkdir outputs
696 $ (
697 > hg -R ./race-repo/ commit -m left-side-commit \
698 > --config "extensions.race=${RUNTESTDIR}/testlib/persistent-nodemap-race-ext.py" \
699 > --config 'devel.nodemap-race.role=left';
700 > touch sync-files/left-done
701 > ) > outputs/left.txt 2>&1 &
702 $ (
703 > hg -R ./other-wc/ commit -m right-side-commit \
704 > --config "extensions.race=${RUNTESTDIR}/testlib/persistent-nodemap-race-ext.py" \
705 > --config 'devel.nodemap-race.role=right';
706 > touch sync-files/right-done
707 > ) > outputs/right.txt 2>&1 &
708 $ (
709 > hg -R ./race-repo/ check-nodemap-race \
710 > --config "extensions.race=${RUNTESTDIR}/testlib/persistent-nodemap-race-ext.py" \
711 > --config 'devel.nodemap-race.role=reader';
712 > touch sync-files/reader-done
713 > ) > outputs/reader.txt 2>&1 &
714 $ sh "$RUNTESTDIR"/testlib/wait-on-file 10 sync-files/left-done
715 $ cat outputs/left.txt
716 docket-details:
717 uid: 43c37dde
718 actual-tip: 5005
719 tip-rev: 5005
720 data-length: 121088
721 nodemap-race: left side locked and ready to commit
722 docket-details:
723 uid: 43c37dde
724 actual-tip: 5005
725 tip-rev: 5005
726 data-length: 121088
727 finalized changelog write
728 persisting changelog nodemap
729 new data start at 121088
730 persisted changelog nodemap
731 docket-details:
732 uid: 43c37dde
733 actual-tip: 5006
734 tip-rev: 5006
735 data-length: 121280
736 $ sh "$RUNTESTDIR"/testlib/wait-on-file 10 sync-files/right-done
737 $ cat outputs/right.txt
738 nodemap-race: right side start of the locking sequence
739 nodemap-race: right side reading changelog
740 nodemap-race: right side reading of changelog is done
741 docket-details:
742 uid: 43c37dde
743 actual-tip: 5006
744 tip-rev: 5005
745 data-length: 121088
746 nodemap-race: right side ready to wait for the lock
747 nodemap-race: right side locked and ready to commit
748 docket-details:
749 uid: 43c37dde
750 actual-tip: 5006
751 tip-rev: 5005
752 data-length: 121088
753 right ready to write, waiting for reader
754 right proceeding with writing its changelog index and nodemap
755 finalized changelog write
756 persisting changelog nodemap
757 new data start at 121088
758 persisted changelog nodemap
759 docket-details:
760 uid: 43c37dde
761 actual-tip: 5007
762 tip-rev: 5007
763 data-length: 121472
764 $ sh "$RUNTESTDIR"/testlib/wait-on-file 10 sync-files/reader-done
765 $ cat outputs/reader.txt
766 reader: reading changelog
767 reader ready to read the changelog, waiting for right
768 reader: nodemap docket read
769 record-data-length: 121280
770 actual-data-length: 121280
771 file-actual-length: 121472
772 reader: changelog read
773 docket-details:
774 uid: 43c37dde
775 actual-tip: 5006
776 tip-rev: 5006
777 data-length: 121280
778 tip-rev: 5006
779 tip-node: 492901161367
780 node-rev: 5006
781 error while checking revision: 18 (known-bad-output !)
782 Inconsistency: Revision 5007 found in nodemap is not in revlog indexi (known-bad-output !)
783
784 $ hg -R ./race-repo log -G -r 'head()'
785 o changeset: 5007:ac4a2abde241
786 | tag: tip
787 ~ parent: 5001:16395c3cf7e2
788 user: test
789 date: Thu Jan 01 00:00:00 1970 +0000
790 summary: right-side-commit
791
792 @ changeset: 5006:492901161367
793 | user: test
794 ~ date: Thu Jan 01 00:00:00 1970 +0000
795 summary: left-side-commit
796
797 $ hg -R ./other-wc log -G -r 'head()'
798 @ changeset: 5007:ac4a2abde241
799 | tag: tip
800 ~ parent: 5001:16395c3cf7e2
801 user: test
802 date: Thu Jan 01 00:00:00 1970 +0000
803 summary: right-side-commit
804
805 o changeset: 5006:492901161367
806 | user: test
807 ~ date: Thu Jan 01 00:00:00 1970 +0000
808 summary: left-side-commit
809
810 #endif
811
616 Test upgrade / downgrade
812 Test upgrade / downgrade
617 ========================
813 ========================
618
814
815 $ cd ./test-repo/
816
619 downgrading
817 downgrading
620
818
621 $ cat << EOF >> .hg/hgrc
819 $ cat << EOF >> .hg/hgrc
622 > [format]
820 > [format]
623 > use-persistent-nodemap=no
821 > use-persistent-nodemap=no
624 > EOF
822 > EOF
625 $ hg debugformat -v
823 $ hg debugformat -v
626 format-variant repo config default
824 format-variant repo config default
627 fncache: yes yes yes
825 fncache: yes yes yes
628 dirstate-v2: no no no
826 dirstate-v2: no no no
629 dotencode: yes yes yes
827 dotencode: yes yes yes
630 generaldelta: yes yes yes
828 generaldelta: yes yes yes
631 share-safe: yes yes no
829 share-safe: yes yes no
632 sparserevlog: yes yes yes
830 sparserevlog: yes yes yes
633 persistent-nodemap: yes no no
831 persistent-nodemap: yes no no
634 copies-sdc: no no no
832 copies-sdc: no no no
635 revlog-v2: no no no
833 revlog-v2: no no no
636 changelog-v2: no no no
834 changelog-v2: no no no
637 plain-cl-delta: yes yes yes
835 plain-cl-delta: yes yes yes
638 compression: zlib zlib zlib (no-zstd !)
836 compression: zlib zlib zlib (no-zstd !)
639 compression: zstd zstd zstd (zstd !)
837 compression: zstd zstd zstd (zstd !)
640 compression-level: default default default
838 compression-level: default default default
641 $ hg debugupgraderepo --run --no-backup --quiet
839 $ hg debugupgraderepo --run --no-backup --quiet
642 upgrade will perform the following actions:
840 upgrade will perform the following actions:
643
841
644 requirements
842 requirements
645 preserved: dotencode, fncache, generaldelta, revlogv1, share-safe, sparserevlog, store (no-zstd no-dirstate-v2 !)
843 preserved: dotencode, fncache, generaldelta, revlogv1, share-safe, sparserevlog, store (no-zstd no-dirstate-v2 !)
646 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd no-dirstate-v2 !)
844 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd no-dirstate-v2 !)
647 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd dirstate-v2 !)
845 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd dirstate-v2 !)
648 removed: persistent-nodemap
846 removed: persistent-nodemap
649
847
650 processed revlogs:
848 processed revlogs:
651 - all-filelogs
849 - all-filelogs
652 - changelog
850 - changelog
653 - manifest
851 - manifest
654
852
655 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
853 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
656 00changelog-*.nd (glob)
854 00changelog-*.nd (glob)
657 00manifest-*.nd (glob)
855 00manifest-*.nd (glob)
658 undo.backup.00changelog.n
856 undo.backup.00changelog.n
659 undo.backup.00manifest.n
857 undo.backup.00manifest.n
660 $ hg debugnodemap --metadata
858 $ hg debugnodemap --metadata
661
859
662
860
663 upgrading
861 upgrading
664
862
665 $ cat << EOF >> .hg/hgrc
863 $ cat << EOF >> .hg/hgrc
666 > [format]
864 > [format]
667 > use-persistent-nodemap=yes
865 > use-persistent-nodemap=yes
668 > EOF
866 > EOF
669 $ hg debugformat -v
867 $ hg debugformat -v
670 format-variant repo config default
868 format-variant repo config default
671 fncache: yes yes yes
869 fncache: yes yes yes
672 dirstate-v2: no no no
870 dirstate-v2: no no no
673 dotencode: yes yes yes
871 dotencode: yes yes yes
674 generaldelta: yes yes yes
872 generaldelta: yes yes yes
675 share-safe: yes yes no
873 share-safe: yes yes no
676 sparserevlog: yes yes yes
874 sparserevlog: yes yes yes
677 persistent-nodemap: no yes no
875 persistent-nodemap: no yes no
678 copies-sdc: no no no
876 copies-sdc: no no no
679 revlog-v2: no no no
877 revlog-v2: no no no
680 changelog-v2: no no no
878 changelog-v2: no no no
681 plain-cl-delta: yes yes yes
879 plain-cl-delta: yes yes yes
682 compression: zlib zlib zlib (no-zstd !)
880 compression: zlib zlib zlib (no-zstd !)
683 compression: zstd zstd zstd (zstd !)
881 compression: zstd zstd zstd (zstd !)
684 compression-level: default default default
882 compression-level: default default default
685 $ hg debugupgraderepo --run --no-backup --quiet
883 $ hg debugupgraderepo --run --no-backup --quiet
686 upgrade will perform the following actions:
884 upgrade will perform the following actions:
687
885
688 requirements
886 requirements
689 preserved: dotencode, fncache, generaldelta, revlogv1, share-safe, sparserevlog, store (no-zstd no-dirstate-v2 !)
887 preserved: dotencode, fncache, generaldelta, revlogv1, share-safe, sparserevlog, store (no-zstd no-dirstate-v2 !)
690 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd no-dirstate-v2 !)
888 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd no-dirstate-v2 !)
691 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd dirstate-v2 !)
889 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd dirstate-v2 !)
692 added: persistent-nodemap
890 added: persistent-nodemap
693
891
694 processed revlogs:
892 processed revlogs:
695 - all-filelogs
893 - all-filelogs
696 - changelog
894 - changelog
697 - manifest
895 - manifest
698
896
699 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
897 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
700 00changelog-*.nd (glob)
898 00changelog-*.nd (glob)
701 00changelog.n
899 00changelog.n
702 00manifest-*.nd (glob)
900 00manifest-*.nd (glob)
703 00manifest.n
901 00manifest.n
704 undo.backup.00changelog.n
902 undo.backup.00changelog.n
705 undo.backup.00manifest.n
903 undo.backup.00manifest.n
706
904
707 $ hg debugnodemap --metadata
905 $ hg debugnodemap --metadata
708 uid: * (glob)
906 uid: * (glob)
709 tip-rev: 5005
907 tip-rev: 5005
710 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
908 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
711 data-length: 121088
909 data-length: 121088
712 data-unused: 0
910 data-unused: 0
713 data-unused: 0.000%
911 data-unused: 0.000%
714
912
715 Running unrelated upgrade
913 Running unrelated upgrade
716
914
717 $ hg debugupgraderepo --run --no-backup --quiet --optimize re-delta-all
915 $ hg debugupgraderepo --run --no-backup --quiet --optimize re-delta-all
718 upgrade will perform the following actions:
916 upgrade will perform the following actions:
719
917
720 requirements
918 requirements
721 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, share-safe, sparserevlog, store (no-zstd no-dirstate-v2 !)
919 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, share-safe, sparserevlog, store (no-zstd no-dirstate-v2 !)
722 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd no-dirstate-v2 !)
920 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd no-dirstate-v2 !)
723 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd dirstate-v2 !)
921 preserved: dotencode, exp-dirstate-v2, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd dirstate-v2 !)
724
922
725 optimisations: re-delta-all
923 optimisations: re-delta-all
726
924
727 processed revlogs:
925 processed revlogs:
728 - all-filelogs
926 - all-filelogs
729 - changelog
927 - changelog
730 - manifest
928 - manifest
731
929
732 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
930 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
733 00changelog-*.nd (glob)
931 00changelog-*.nd (glob)
734 00changelog.n
932 00changelog.n
735 00manifest-*.nd (glob)
933 00manifest-*.nd (glob)
736 00manifest.n
934 00manifest.n
737
935
738 $ hg debugnodemap --metadata
936 $ hg debugnodemap --metadata
739 uid: * (glob)
937 uid: * (glob)
740 tip-rev: 5005
938 tip-rev: 5005
741 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
939 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
742 data-length: 121088
940 data-length: 121088
743 data-unused: 0
941 data-unused: 0
744 data-unused: 0.000%
942 data-unused: 0.000%
745
943
746 Persistent nodemap and local/streaming clone
944 Persistent nodemap and local/streaming clone
747 ============================================
945 ============================================
748
946
749 $ cd ..
947 $ cd ..
750
948
751 standard clone
949 standard clone
752 --------------
950 --------------
753
951
754 The persistent nodemap should exist after a streaming clone
952 The persistent nodemap should exist after a streaming clone
755
953
756 $ hg clone --pull --quiet -U test-repo standard-clone
954 $ hg clone --pull --quiet -U test-repo standard-clone
757 $ ls -1 standard-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
955 $ ls -1 standard-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
758 00changelog-*.nd (glob)
956 00changelog-*.nd (glob)
759 00changelog.n
957 00changelog.n
760 00manifest-*.nd (glob)
958 00manifest-*.nd (glob)
761 00manifest.n
959 00manifest.n
762 $ hg -R standard-clone debugnodemap --metadata
960 $ hg -R standard-clone debugnodemap --metadata
763 uid: * (glob)
961 uid: * (glob)
764 tip-rev: 5005
962 tip-rev: 5005
765 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
963 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
766 data-length: 121088
964 data-length: 121088
767 data-unused: 0
965 data-unused: 0
768 data-unused: 0.000%
966 data-unused: 0.000%
769
967
770
968
771 local clone
969 local clone
772 ------------
970 ------------
773
971
774 The persistent nodemap should exist after a streaming clone
972 The persistent nodemap should exist after a streaming clone
775
973
776 $ hg clone -U test-repo local-clone
974 $ hg clone -U test-repo local-clone
777 $ ls -1 local-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
975 $ ls -1 local-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
778 00changelog-*.nd (glob)
976 00changelog-*.nd (glob)
779 00changelog.n
977 00changelog.n
780 00manifest-*.nd (glob)
978 00manifest-*.nd (glob)
781 00manifest.n
979 00manifest.n
782 $ hg -R local-clone debugnodemap --metadata
980 $ hg -R local-clone debugnodemap --metadata
783 uid: * (glob)
981 uid: * (glob)
784 tip-rev: 5005
982 tip-rev: 5005
785 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
983 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
786 data-length: 121088
984 data-length: 121088
787 data-unused: 0
985 data-unused: 0
788 data-unused: 0.000%
986 data-unused: 0.000%
789
987
790 Test various corruption case
988 Test various corruption case
791 ============================
989 ============================
792
990
793 Missing datafile
991 Missing datafile
794 ----------------
992 ----------------
795
993
796 Test behavior with a missing datafile
994 Test behavior with a missing datafile
797
995
798 $ hg clone --quiet --pull test-repo corruption-test-repo
996 $ hg clone --quiet --pull test-repo corruption-test-repo
799 $ ls -1 corruption-test-repo/.hg/store/00changelog*
997 $ ls -1 corruption-test-repo/.hg/store/00changelog*
800 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
998 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
801 corruption-test-repo/.hg/store/00changelog.d
999 corruption-test-repo/.hg/store/00changelog.d
802 corruption-test-repo/.hg/store/00changelog.i
1000 corruption-test-repo/.hg/store/00changelog.i
803 corruption-test-repo/.hg/store/00changelog.n
1001 corruption-test-repo/.hg/store/00changelog.n
804 $ rm corruption-test-repo/.hg/store/00changelog*.nd
1002 $ rm corruption-test-repo/.hg/store/00changelog*.nd
805 $ hg log -R corruption-test-repo -r .
1003 $ hg log -R corruption-test-repo -r .
806 changeset: 5005:90d5d3ba2fc4
1004 changeset: 5005:90d5d3ba2fc4
807 tag: tip
1005 tag: tip
808 user: test
1006 user: test
809 date: Thu Jan 01 00:00:00 1970 +0000
1007 date: Thu Jan 01 00:00:00 1970 +0000
810 summary: a2
1008 summary: a2
811
1009
812 $ ls -1 corruption-test-repo/.hg/store/00changelog*
1010 $ ls -1 corruption-test-repo/.hg/store/00changelog*
813 corruption-test-repo/.hg/store/00changelog.d
1011 corruption-test-repo/.hg/store/00changelog.d
814 corruption-test-repo/.hg/store/00changelog.i
1012 corruption-test-repo/.hg/store/00changelog.i
815 corruption-test-repo/.hg/store/00changelog.n
1013 corruption-test-repo/.hg/store/00changelog.n
816
1014
817 Truncated data file
1015 Truncated data file
818 -------------------
1016 -------------------
819
1017
820 Test behavior with a too short datafile
1018 Test behavior with a too short datafile
821
1019
822 rebuild the missing data
1020 rebuild the missing data
823 $ hg -R corruption-test-repo debugupdatecache
1021 $ hg -R corruption-test-repo debugupdatecache
824 $ ls -1 corruption-test-repo/.hg/store/00changelog*
1022 $ ls -1 corruption-test-repo/.hg/store/00changelog*
825 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
1023 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
826 corruption-test-repo/.hg/store/00changelog.d
1024 corruption-test-repo/.hg/store/00changelog.d
827 corruption-test-repo/.hg/store/00changelog.i
1025 corruption-test-repo/.hg/store/00changelog.i
828 corruption-test-repo/.hg/store/00changelog.n
1026 corruption-test-repo/.hg/store/00changelog.n
829
1027
830 truncate the file
1028 truncate the file
831
1029
832 $ datafilepath=`ls corruption-test-repo/.hg/store/00changelog*.nd`
1030 $ datafilepath=`ls corruption-test-repo/.hg/store/00changelog*.nd`
833 $ f -s $datafilepath
1031 $ f -s $datafilepath
834 corruption-test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
1032 corruption-test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
835 $ dd if=$datafilepath bs=1000 count=10 of=$datafilepath-tmp status=noxfer
1033 $ dd if=$datafilepath bs=1000 count=10 of=$datafilepath-tmp status=noxfer
836 10+0 records in
1034 10+0 records in
837 10+0 records out
1035 10+0 records out
838 $ mv $datafilepath-tmp $datafilepath
1036 $ mv $datafilepath-tmp $datafilepath
839 $ f -s $datafilepath
1037 $ f -s $datafilepath
840 corruption-test-repo/.hg/store/00changelog-*.nd: size=10000 (glob)
1038 corruption-test-repo/.hg/store/00changelog-*.nd: size=10000 (glob)
841
1039
842 Check that Mercurial reaction to this event
1040 Check that Mercurial reaction to this event
843
1041
844 $ hg -R corruption-test-repo log -r . --traceback
1042 $ hg -R corruption-test-repo log -r . --traceback
845 changeset: 5005:90d5d3ba2fc4
1043 changeset: 5005:90d5d3ba2fc4
846 tag: tip
1044 tag: tip
847 user: test
1045 user: test
848 date: Thu Jan 01 00:00:00 1970 +0000
1046 date: Thu Jan 01 00:00:00 1970 +0000
849 summary: a2
1047 summary: a2
850
1048
851
1049
852
1050
853 stream clone
1051 stream clone
854 ============
1052 ============
855
1053
856 The persistent nodemap should exist after a streaming clone
1054 The persistent nodemap should exist after a streaming clone
857
1055
858 Simple case
1056 Simple case
859 -----------
1057 -----------
860
1058
861 No race condition
1059 No race condition
862
1060
863 $ hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone --debug | egrep '00(changelog|manifest)'
1061 $ hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone --debug | egrep '00(changelog|manifest)'
864 adding [s] 00manifest.n (62 bytes)
1062 adding [s] 00manifest.n (62 bytes)
865 adding [s] 00manifest-*.nd (118 KB) (glob)
1063 adding [s] 00manifest-*.nd (118 KB) (glob)
866 adding [s] 00changelog.n (62 bytes)
1064 adding [s] 00changelog.n (62 bytes)
867 adding [s] 00changelog-*.nd (118 KB) (glob)
1065 adding [s] 00changelog-*.nd (118 KB) (glob)
868 adding [s] 00manifest.d (452 KB) (no-zstd !)
1066 adding [s] 00manifest.d (452 KB) (no-zstd !)
869 adding [s] 00manifest.d (491 KB) (zstd !)
1067 adding [s] 00manifest.d (491 KB) (zstd !)
870 adding [s] 00changelog.d (360 KB) (no-zstd !)
1068 adding [s] 00changelog.d (360 KB) (no-zstd !)
871 adding [s] 00changelog.d (368 KB) (zstd !)
1069 adding [s] 00changelog.d (368 KB) (zstd !)
872 adding [s] 00manifest.i (313 KB)
1070 adding [s] 00manifest.i (313 KB)
873 adding [s] 00changelog.i (313 KB)
1071 adding [s] 00changelog.i (313 KB)
874 $ ls -1 stream-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
1072 $ ls -1 stream-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
875 00changelog-*.nd (glob)
1073 00changelog-*.nd (glob)
876 00changelog.n
1074 00changelog.n
877 00manifest-*.nd (glob)
1075 00manifest-*.nd (glob)
878 00manifest.n
1076 00manifest.n
879 $ hg -R stream-clone debugnodemap --metadata
1077 $ hg -R stream-clone debugnodemap --metadata
880 uid: * (glob)
1078 uid: * (glob)
881 tip-rev: 5005
1079 tip-rev: 5005
882 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
1080 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
883 data-length: 121088
1081 data-length: 121088
884 data-unused: 0
1082 data-unused: 0
885 data-unused: 0.000%
1083 data-unused: 0.000%
886
1084
887 new data appened
1085 new data appened
888 -----------------
1086 -----------------
889
1087
890 Other commit happening on the server during the stream clone
1088 Other commit happening on the server during the stream clone
891
1089
892 setup the step-by-step stream cloning
1090 setup the step-by-step stream cloning
893
1091
894 $ HG_TEST_STREAM_WALKED_FILE_1="$TESTTMP/sync_file_walked_1"
1092 $ HG_TEST_STREAM_WALKED_FILE_1="$TESTTMP/sync_file_walked_1"
895 $ export HG_TEST_STREAM_WALKED_FILE_1
1093 $ export HG_TEST_STREAM_WALKED_FILE_1
896 $ HG_TEST_STREAM_WALKED_FILE_2="$TESTTMP/sync_file_walked_2"
1094 $ HG_TEST_STREAM_WALKED_FILE_2="$TESTTMP/sync_file_walked_2"
897 $ export HG_TEST_STREAM_WALKED_FILE_2
1095 $ export HG_TEST_STREAM_WALKED_FILE_2
898 $ HG_TEST_STREAM_WALKED_FILE_3="$TESTTMP/sync_file_walked_3"
1096 $ HG_TEST_STREAM_WALKED_FILE_3="$TESTTMP/sync_file_walked_3"
899 $ export HG_TEST_STREAM_WALKED_FILE_3
1097 $ export HG_TEST_STREAM_WALKED_FILE_3
900 $ cat << EOF >> test-repo/.hg/hgrc
1098 $ cat << EOF >> test-repo/.hg/hgrc
901 > [extensions]
1099 > [extensions]
902 > steps=$RUNTESTDIR/testlib/ext-stream-clone-steps.py
1100 > steps=$RUNTESTDIR/testlib/ext-stream-clone-steps.py
903 > EOF
1101 > EOF
904
1102
905 Check and record file state beforehand
1103 Check and record file state beforehand
906
1104
907 $ f --size test-repo/.hg/store/00changelog*
1105 $ f --size test-repo/.hg/store/00changelog*
908 test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
1106 test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
909 test-repo/.hg/store/00changelog.d: size=376891 (zstd !)
1107 test-repo/.hg/store/00changelog.d: size=376891 (zstd !)
910 test-repo/.hg/store/00changelog.d: size=368890 (no-zstd !)
1108 test-repo/.hg/store/00changelog.d: size=368890 (no-zstd !)
911 test-repo/.hg/store/00changelog.i: size=320384
1109 test-repo/.hg/store/00changelog.i: size=320384
912 test-repo/.hg/store/00changelog.n: size=62
1110 test-repo/.hg/store/00changelog.n: size=62
913 $ hg -R test-repo debugnodemap --metadata | tee server-metadata.txt
1111 $ hg -R test-repo debugnodemap --metadata | tee server-metadata.txt
914 uid: * (glob)
1112 uid: * (glob)
915 tip-rev: 5005
1113 tip-rev: 5005
916 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
1114 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
917 data-length: 121088
1115 data-length: 121088
918 data-unused: 0
1116 data-unused: 0
919 data-unused: 0.000%
1117 data-unused: 0.000%
920
1118
921 Prepare a commit
1119 Prepare a commit
922
1120
923 $ echo foo >> test-repo/foo
1121 $ echo foo >> test-repo/foo
924 $ hg -R test-repo/ add test-repo/foo
1122 $ hg -R test-repo/ add test-repo/foo
925
1123
926 Do a mix of clone and commit at the same time so that the file listed on disk differ at actual transfer time.
1124 Do a mix of clone and commit at the same time so that the file listed on disk differ at actual transfer time.
927
1125
928 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-1 --debug 2>> clone-output | egrep '00(changelog|manifest)' >> clone-output; touch $HG_TEST_STREAM_WALKED_FILE_3) &
1126 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-1 --debug 2>> clone-output | egrep '00(changelog|manifest)' >> clone-output; touch $HG_TEST_STREAM_WALKED_FILE_3) &
929 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
1127 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
930 $ hg -R test-repo/ commit -m foo
1128 $ hg -R test-repo/ commit -m foo
931 $ touch $HG_TEST_STREAM_WALKED_FILE_2
1129 $ touch $HG_TEST_STREAM_WALKED_FILE_2
932 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
1130 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
933 $ cat clone-output
1131 $ cat clone-output
934 adding [s] 00manifest.n (62 bytes)
1132 adding [s] 00manifest.n (62 bytes)
935 adding [s] 00manifest-*.nd (118 KB) (glob)
1133 adding [s] 00manifest-*.nd (118 KB) (glob)
936 adding [s] 00changelog.n (62 bytes)
1134 adding [s] 00changelog.n (62 bytes)
937 adding [s] 00changelog-*.nd (118 KB) (glob)
1135 adding [s] 00changelog-*.nd (118 KB) (glob)
938 adding [s] 00manifest.d (452 KB) (no-zstd !)
1136 adding [s] 00manifest.d (452 KB) (no-zstd !)
939 adding [s] 00manifest.d (491 KB) (zstd !)
1137 adding [s] 00manifest.d (491 KB) (zstd !)
940 adding [s] 00changelog.d (360 KB) (no-zstd !)
1138 adding [s] 00changelog.d (360 KB) (no-zstd !)
941 adding [s] 00changelog.d (368 KB) (zstd !)
1139 adding [s] 00changelog.d (368 KB) (zstd !)
942 adding [s] 00manifest.i (313 KB)
1140 adding [s] 00manifest.i (313 KB)
943 adding [s] 00changelog.i (313 KB)
1141 adding [s] 00changelog.i (313 KB)
944
1142
945 Check the result state
1143 Check the result state
946
1144
947 $ f --size stream-clone-race-1/.hg/store/00changelog*
1145 $ f --size stream-clone-race-1/.hg/store/00changelog*
948 stream-clone-race-1/.hg/store/00changelog-*.nd: size=121088 (glob)
1146 stream-clone-race-1/.hg/store/00changelog-*.nd: size=121088 (glob)
949 stream-clone-race-1/.hg/store/00changelog.d: size=368890 (no-zstd !)
1147 stream-clone-race-1/.hg/store/00changelog.d: size=368890 (no-zstd !)
950 stream-clone-race-1/.hg/store/00changelog.d: size=376891 (zstd !)
1148 stream-clone-race-1/.hg/store/00changelog.d: size=376891 (zstd !)
951 stream-clone-race-1/.hg/store/00changelog.i: size=320384
1149 stream-clone-race-1/.hg/store/00changelog.i: size=320384
952 stream-clone-race-1/.hg/store/00changelog.n: size=62
1150 stream-clone-race-1/.hg/store/00changelog.n: size=62
953
1151
954 $ hg -R stream-clone-race-1 debugnodemap --metadata | tee client-metadata.txt
1152 $ hg -R stream-clone-race-1 debugnodemap --metadata | tee client-metadata.txt
955 uid: * (glob)
1153 uid: * (glob)
956 tip-rev: 5005
1154 tip-rev: 5005
957 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
1155 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
958 data-length: 121088
1156 data-length: 121088
959 data-unused: 0
1157 data-unused: 0
960 data-unused: 0.000%
1158 data-unused: 0.000%
961
1159
962 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
1160 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
963 (ie: the following diff should be empty)
1161 (ie: the following diff should be empty)
964
1162
965 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
1163 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
966
1164
967 #if no-rust no-pure
1165 #if no-rust no-pure
968 $ diff -u server-metadata.txt client-metadata.txt
1166 $ diff -u server-metadata.txt client-metadata.txt
969 --- server-metadata.txt * (glob)
1167 --- server-metadata.txt * (glob)
970 +++ client-metadata.txt * (glob)
1168 +++ client-metadata.txt * (glob)
971 @@ -1,4 +1,4 @@
1169 @@ -1,4 +1,4 @@
972 -uid: * (glob)
1170 -uid: * (glob)
973 +uid: * (glob)
1171 +uid: * (glob)
974 tip-rev: 5005
1172 tip-rev: 5005
975 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
1173 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
976 data-length: 121088
1174 data-length: 121088
977 [1]
1175 [1]
978 #else
1176 #else
979 $ diff -u server-metadata.txt client-metadata.txt
1177 $ diff -u server-metadata.txt client-metadata.txt
980 #endif
1178 #endif
981
1179
982
1180
983 Clean up after the test.
1181 Clean up after the test.
984
1182
985 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_1"
1183 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_1"
986 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_2"
1184 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_2"
987 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_3"
1185 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_3"
988
1186
989 full regeneration
1187 full regeneration
990 -----------------
1188 -----------------
991
1189
992 A full nodemap is generated
1190 A full nodemap is generated
993
1191
994 (ideally this test would append enough data to make sure the nodemap data file
1192 (ideally this test would append enough data to make sure the nodemap data file
995 get changed, however to make thing simpler we will force the regeneration for
1193 get changed, however to make thing simpler we will force the regeneration for
996 this test.
1194 this test.
997
1195
998 Check the initial state
1196 Check the initial state
999
1197
1000 $ f --size test-repo/.hg/store/00changelog*
1198 $ f --size test-repo/.hg/store/00changelog*
1001 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1199 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1002 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1200 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1003 test-repo/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1201 test-repo/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1004 test-repo/.hg/store/00changelog.d: size=376950 (zstd !)
1202 test-repo/.hg/store/00changelog.d: size=376950 (zstd !)
1005 test-repo/.hg/store/00changelog.d: size=368949 (no-zstd !)
1203 test-repo/.hg/store/00changelog.d: size=368949 (no-zstd !)
1006 test-repo/.hg/store/00changelog.i: size=320448
1204 test-repo/.hg/store/00changelog.i: size=320448
1007 test-repo/.hg/store/00changelog.n: size=62
1205 test-repo/.hg/store/00changelog.n: size=62
1008 $ hg -R test-repo debugnodemap --metadata | tee server-metadata-2.txt
1206 $ hg -R test-repo debugnodemap --metadata | tee server-metadata-2.txt
1009 uid: * (glob)
1207 uid: * (glob)
1010 tip-rev: 5006
1208 tip-rev: 5006
1011 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1209 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1012 data-length: 121344 (rust !)
1210 data-length: 121344 (rust !)
1013 data-length: 121344 (pure !)
1211 data-length: 121344 (pure !)
1014 data-length: 121152 (no-rust no-pure !)
1212 data-length: 121152 (no-rust no-pure !)
1015 data-unused: 192 (rust !)
1213 data-unused: 192 (rust !)
1016 data-unused: 192 (pure !)
1214 data-unused: 192 (pure !)
1017 data-unused: 0 (no-rust no-pure !)
1215 data-unused: 0 (no-rust no-pure !)
1018 data-unused: 0.158% (rust !)
1216 data-unused: 0.158% (rust !)
1019 data-unused: 0.158% (pure !)
1217 data-unused: 0.158% (pure !)
1020 data-unused: 0.000% (no-rust no-pure !)
1218 data-unused: 0.000% (no-rust no-pure !)
1021
1219
1022 Performe the mix of clone and full refresh of the nodemap, so that the files
1220 Performe the mix of clone and full refresh of the nodemap, so that the files
1023 (and filenames) are different between listing time and actual transfer time.
1221 (and filenames) are different between listing time and actual transfer time.
1024
1222
1025 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-2 --debug 2>> clone-output-2 | egrep '00(changelog|manifest)' >> clone-output-2; touch $HG_TEST_STREAM_WALKED_FILE_3) &
1223 $ (hg clone -U --stream --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/test-repo stream-clone-race-2 --debug 2>> clone-output-2 | egrep '00(changelog|manifest)' >> clone-output-2; touch $HG_TEST_STREAM_WALKED_FILE_3) &
1026 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
1224 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
1027 $ rm test-repo/.hg/store/00changelog.n
1225 $ rm test-repo/.hg/store/00changelog.n
1028 $ rm test-repo/.hg/store/00changelog-*.nd
1226 $ rm test-repo/.hg/store/00changelog-*.nd
1029 $ hg -R test-repo/ debugupdatecache
1227 $ hg -R test-repo/ debugupdatecache
1030 $ touch $HG_TEST_STREAM_WALKED_FILE_2
1228 $ touch $HG_TEST_STREAM_WALKED_FILE_2
1031 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
1229 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
1032
1230
1033 (note: the stream clone code wronly pick the `undo.` files)
1231 (note: the stream clone code wronly pick the `undo.` files)
1034
1232
1035 $ cat clone-output-2
1233 $ cat clone-output-2
1036 adding [s] undo.backup.00manifest.n (62 bytes) (known-bad-output !)
1234 adding [s] undo.backup.00manifest.n (62 bytes) (known-bad-output !)
1037 adding [s] undo.backup.00changelog.n (62 bytes) (known-bad-output !)
1235 adding [s] undo.backup.00changelog.n (62 bytes) (known-bad-output !)
1038 adding [s] 00manifest.n (62 bytes)
1236 adding [s] 00manifest.n (62 bytes)
1039 adding [s] 00manifest-*.nd (118 KB) (glob)
1237 adding [s] 00manifest-*.nd (118 KB) (glob)
1040 adding [s] 00changelog.n (62 bytes)
1238 adding [s] 00changelog.n (62 bytes)
1041 adding [s] 00changelog-*.nd (118 KB) (glob)
1239 adding [s] 00changelog-*.nd (118 KB) (glob)
1042 adding [s] 00manifest.d (492 KB) (zstd !)
1240 adding [s] 00manifest.d (492 KB) (zstd !)
1043 adding [s] 00manifest.d (452 KB) (no-zstd !)
1241 adding [s] 00manifest.d (452 KB) (no-zstd !)
1044 adding [s] 00changelog.d (360 KB) (no-zstd !)
1242 adding [s] 00changelog.d (360 KB) (no-zstd !)
1045 adding [s] 00changelog.d (368 KB) (zstd !)
1243 adding [s] 00changelog.d (368 KB) (zstd !)
1046 adding [s] 00manifest.i (313 KB)
1244 adding [s] 00manifest.i (313 KB)
1047 adding [s] 00changelog.i (313 KB)
1245 adding [s] 00changelog.i (313 KB)
1048
1246
1049 Check the result.
1247 Check the result.
1050
1248
1051 $ f --size stream-clone-race-2/.hg/store/00changelog*
1249 $ f --size stream-clone-race-2/.hg/store/00changelog*
1052 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1250 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1053 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1251 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1054 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1252 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1055 stream-clone-race-2/.hg/store/00changelog.d: size=376950 (zstd !)
1253 stream-clone-race-2/.hg/store/00changelog.d: size=376950 (zstd !)
1056 stream-clone-race-2/.hg/store/00changelog.d: size=368949 (no-zstd !)
1254 stream-clone-race-2/.hg/store/00changelog.d: size=368949 (no-zstd !)
1057 stream-clone-race-2/.hg/store/00changelog.i: size=320448
1255 stream-clone-race-2/.hg/store/00changelog.i: size=320448
1058 stream-clone-race-2/.hg/store/00changelog.n: size=62
1256 stream-clone-race-2/.hg/store/00changelog.n: size=62
1059
1257
1060 $ hg -R stream-clone-race-2 debugnodemap --metadata | tee client-metadata-2.txt
1258 $ hg -R stream-clone-race-2 debugnodemap --metadata | tee client-metadata-2.txt
1061 uid: * (glob)
1259 uid: * (glob)
1062 tip-rev: 5006
1260 tip-rev: 5006
1063 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1261 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1064 data-length: 121344 (rust !)
1262 data-length: 121344 (rust !)
1065 data-unused: 192 (rust !)
1263 data-unused: 192 (rust !)
1066 data-unused: 0.158% (rust !)
1264 data-unused: 0.158% (rust !)
1067 data-length: 121152 (no-rust no-pure !)
1265 data-length: 121152 (no-rust no-pure !)
1068 data-unused: 0 (no-rust no-pure !)
1266 data-unused: 0 (no-rust no-pure !)
1069 data-unused: 0.000% (no-rust no-pure !)
1267 data-unused: 0.000% (no-rust no-pure !)
1070 data-length: 121344 (pure !)
1268 data-length: 121344 (pure !)
1071 data-unused: 192 (pure !)
1269 data-unused: 192 (pure !)
1072 data-unused: 0.158% (pure !)
1270 data-unused: 0.158% (pure !)
1073
1271
1074 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
1272 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
1075 (ie: the following diff should be empty)
1273 (ie: the following diff should be empty)
1076
1274
1077 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
1275 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
1078
1276
1079 #if no-rust no-pure
1277 #if no-rust no-pure
1080 $ diff -u server-metadata-2.txt client-metadata-2.txt
1278 $ diff -u server-metadata-2.txt client-metadata-2.txt
1081 --- server-metadata-2.txt * (glob)
1279 --- server-metadata-2.txt * (glob)
1082 +++ client-metadata-2.txt * (glob)
1280 +++ client-metadata-2.txt * (glob)
1083 @@ -1,4 +1,4 @@
1281 @@ -1,4 +1,4 @@
1084 -uid: * (glob)
1282 -uid: * (glob)
1085 +uid: * (glob)
1283 +uid: * (glob)
1086 tip-rev: 5006
1284 tip-rev: 5006
1087 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1285 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1088 data-length: 121152
1286 data-length: 121152
1089 [1]
1287 [1]
1090 #else
1288 #else
1091 $ diff -u server-metadata-2.txt client-metadata-2.txt
1289 $ diff -u server-metadata-2.txt client-metadata-2.txt
1092 #endif
1290 #endif
1093
1291
1094 Clean up after the test
1292 Clean up after the test
1095
1293
1096 $ rm -f $HG_TEST_STREAM_WALKED_FILE_1
1294 $ rm -f $HG_TEST_STREAM_WALKED_FILE_1
1097 $ rm -f $HG_TEST_STREAM_WALKED_FILE_2
1295 $ rm -f $HG_TEST_STREAM_WALKED_FILE_2
1098 $ rm -f $HG_TEST_STREAM_WALKED_FILE_3
1296 $ rm -f $HG_TEST_STREAM_WALKED_FILE_3
1099
1297
General Comments 0
You need to be logged in to leave comments. Login now