##// END OF EJS Templates
dirstate-v2: add devel config option to control write behavior...
Raphaël Gomès -
r51117:ecd28d89 stable
parent child Browse files
Show More
@@ -640,6 +640,15 b' coreconfigitem('
640 640 b'deprec-warn',
641 641 default=False,
642 642 )
643 # possible values:
644 # - auto (the default)
645 # - force-append
646 # - force-new
647 coreconfigitem(
648 b'devel',
649 b'dirstate.v2.data_update_mode',
650 default="auto",
651 )
643 652 coreconfigitem(
644 653 b'devel',
645 654 b'disableloaddefaultcerts',
@@ -33,6 +33,7 b' rangemask = 0x7FFFFFFF'
33 33
34 34 WRITE_MODE_AUTO = 0
35 35 WRITE_MODE_FORCE_NEW = 1
36 WRITE_MODE_FORCE_APPEND = 2
36 37
37 38
38 39 class _dirstatemapcommon:
@@ -57,6 +58,16 b' class _dirstatemapcommon:'
57 58 self._parents = None
58 59 self._dirtyparents = False
59 60 self._docket = None
61 write_mode = ui.config(b"devel", b"dirstate.v2.data_update_mode")
62 if write_mode == b"auto":
63 self._write_mode = WRITE_MODE_AUTO
64 elif write_mode == b"force-append":
65 self._write_mode = WRITE_MODE_FORCE_APPEND
66 elif write_mode == b"force-new":
67 self._write_mode = WRITE_MODE_FORCE_NEW
68 else:
69 # unknown value, fallback to default
70 self._write_mode = WRITE_MODE_AUTO
60 71
61 72 # for consistent view between _pl() and _read() invocations
62 73 self._pendingmode = None
@@ -612,7 +623,7 b' if rustmod is not None:'
612 623 return
613 624
614 625 # We can only append to an existing data file if there is one
615 write_mode = WRITE_MODE_AUTO
626 write_mode = self._write_mode
616 627 if self.docket.uuid is None:
617 628 write_mode = WRITE_MODE_FORCE_NEW
618 629 packed, meta, append = self._map.write_v2(write_mode)
@@ -42,6 +42,7 b' pub enum DirstateVersion {'
42 42 pub enum DirstateMapWriteMode {
43 43 Auto,
44 44 ForceNewDataFile,
45 ForceAppend,
45 46 }
46 47
47 48 #[derive(Debug)]
@@ -69,6 +70,9 b" pub struct DirstateMap<'on_disk> {"
69 70 pub(super) old_data_size: usize,
70 71
71 72 pub(super) dirstate_version: DirstateVersion,
73
74 /// Controlled by config option `devel.dirstate.v2.data_update_mode`
75 pub(super) write_mode: DirstateMapWriteMode,
72 76 }
73 77
74 78 /// Using a plain `HgPathBuf` of the full path from the repository root as a
@@ -457,6 +461,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
457 461 unreachable_bytes: 0,
458 462 old_data_size: 0,
459 463 dirstate_version: DirstateVersion::V1,
464 write_mode: DirstateMapWriteMode::Auto,
460 465 }
461 466 }
462 467
@@ -525,8 +530,15 b" impl<'on_disk> DirstateMap<'on_disk> {"
525 530 /// append to the existing data file that contains `self.on_disk` (true),
526 531 /// or create a new data file from scratch (false).
527 532 pub(super) fn write_should_append(&self) -> bool {
528 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
529 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
533 match self.write_mode {
534 DirstateMapWriteMode::ForceAppend => true,
535 DirstateMapWriteMode::ForceNewDataFile => false,
536 DirstateMapWriteMode::Auto => {
537 let ratio =
538 self.unreachable_bytes as f32 / self.on_disk.len() as f32;
539 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
540 }
541 }
530 542 }
531 543
532 544 fn get_node<'tree>(
@@ -923,6 +935,10 b" impl<'on_disk> DirstateMap<'on_disk> {"
923 935 *unreachable_bytes += path.len() as u32
924 936 }
925 937 }
938
939 pub(crate) fn set_write_mode(&mut self, write_mode: DirstateMapWriteMode) {
940 self.write_mode = write_mode;
941 }
926 942 }
927 943
928 944 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
@@ -313,6 +313,7 b" pub(super) fn read<'on_disk>("
313 313 unreachable_bytes: meta.unreachable_bytes.get(),
314 314 old_data_size: on_disk.len(),
315 315 dirstate_version: DirstateVersion::V2,
316 write_mode: DirstateMapWriteMode::Auto,
316 317 };
317 318 Ok(dirstate_map)
318 319 }
@@ -641,6 +642,7 b' pub(super) fn write('
641 642 let append = match write_mode {
642 643 DirstateMapWriteMode::Auto => dirstate_map.write_should_append(),
643 644 DirstateMapWriteMode::ForceNewDataFile => false,
645 DirstateMapWriteMode::ForceAppend => true,
644 646 };
645 647 if append {
646 648 log::trace!("appending to the dirstate data file");
@@ -321,22 +321,39 b' impl Repo {'
321 321 .set(Some(docket.uuid.to_owned()));
322 322 let data_size = docket.data_size();
323 323 let metadata = docket.tree_metadata();
324 if crate::vfs::is_on_nfs_mount(docket.data_filename()) {
325 // Don't mmap on NFS to prevent `SIGBUS` error on deletion
326 OwningDirstateMap::new_v2(
327 self.hg_vfs().read(docket.data_filename())?,
328 data_size,
329 metadata,
330 )
331 } else if let Some(data_mmap) = self
332 .hg_vfs()
333 .mmap_open(docket.data_filename())
334 .io_not_found_as_none()?
335 {
336 OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
337 } else {
338 OwningDirstateMap::new_v2(Vec::new(), data_size, metadata)
339 }
324 let mut map =
325 if crate::vfs::is_on_nfs_mount(docket.data_filename()) {
326 // Don't mmap on NFS to prevent `SIGBUS` error on deletion
327 OwningDirstateMap::new_v2(
328 self.hg_vfs().read(docket.data_filename())?,
329 data_size,
330 metadata,
331 )
332 } else if let Some(data_mmap) = self
333 .hg_vfs()
334 .mmap_open(docket.data_filename())
335 .io_not_found_as_none()?
336 {
337 OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
338 } else {
339 OwningDirstateMap::new_v2(Vec::new(), data_size, metadata)
340 }?;
341
342 let write_mode_config = self
343 .config()
344 .get_str(b"devel", b"dirstate.v2.data_update_mode")
345 .unwrap_or(Some("auto"))
346 .unwrap_or("auto"); // don't bother for devel options
347 let write_mode = match write_mode_config {
348 "auto" => DirstateMapWriteMode::Auto,
349 "force-new" => DirstateMapWriteMode::ForceNewDataFile,
350 "force-append" => DirstateMapWriteMode::ForceAppend,
351 _ => DirstateMapWriteMode::Auto,
352 };
353
354 map.with_dmap_mut(|m| m.set_write_mode(write_mode));
355
356 Ok(map)
340 357 } else {
341 358 let (map, parents) =
342 359 OwningDirstateMap::new_v1(dirstate_file_contents)?;
@@ -254,6 +254,7 b' py_class!(pub class DirstateMap |py| {'
254 254 let rust_write_mode = match write_mode {
255 255 0 => DirstateMapWriteMode::Auto,
256 256 1 => DirstateMapWriteMode::ForceNewDataFile,
257 2 => DirstateMapWriteMode::ForceAppend,
257 258 _ => DirstateMapWriteMode::Auto, // XXX should we error out?
258 259 };
259 260 let result = inner.pack_v2(rust_write_mode);
@@ -93,7 +93,7 b' Add some unknown files and refresh the d'
93 93 $ hg add dir/o
94 94 $ hg remove dir/nested/m
95 95
96 $ hg st
96 $ hg st --config devel.dirstate.v2.data_update_mode=force-new
97 97 A dir/o
98 98 R dir/nested/m
99 99 ? dir/n
@@ -211,8 +211,223 b' Check that unused bytes counter is reset'
211 211
212 212 #endif
213 213
214 (non-Rust always rewrites)
215
216 Test the devel option to control write behavior
217 ==============================================
218
219 Sometimes, debugging or testing the dirstate requires making sure that we have
220 done a complete rewrite of the data file and have no unreachable data around,
221 sometimes it requires we ensure we don't.
222
223 We test the option to force this rewrite by creating the situation where an
224 append would happen and check that it doesn't happen.
225
226 $ cd ..
227 $ hg init force-base
228 $ cd force-base
229 $ mkdir -p dir/nested dir2
230 $ touch -t 200001010000 f dir/nested/a dir/b dir/c dir/d dir2/e dir/nested dir dir2
231 $ hg commit -Aqm "recreate a bunch of files to facilitate append"
232 $ hg st --config devel.dirstate.v2.data_update_mode=force-new
233 $ cd ..
234
235 #if dirstate-v2
236 $ hg -R force-base debugstate --docket | grep unused
237 number of unused bytes: 0
238
239 Check with the option in "auto" mode
240 ------------------------------------
241 $ cp -a force-base append-mostly-no-force-rewrite
242 $ cd append-mostly-no-force-rewrite
243 $ current_uid=$(find_dirstate_uuid)
244
245 Change mtime of dir on disk which will be recorded, causing a small enough change
246 to warrant only an append
247
248 $ touch -t 202212010000 dir2
249 $ hg st \
250 > --config rhg.on-unsupported=abort \
251 > --config devel.dirstate.v2.data_update_mode=auto
252
253 UUID hasn't changed and a non-zero number of unused bytes means we've appended
254
255 $ dirstate_uuid_has_not_changed
256 not testing because using Python implementation (no-rust no-rhg !)
257
258 #if no-rust no-rhg
259 The pure python implementation never appends at the time this is written.
260 $ hg debugstate --docket | grep unused
261 number of unused bytes: 0 (known-bad-output !)
262 #else
263 $ hg debugstate --docket | grep unused
264 number of unused bytes: [1-9]\d* (re)
265 #endif
266 $ cd ..
267
268 Check the same scenario with the option set to "force-new"
269 ---------------------------------------------------------
270
271 $ cp -a force-base append-mostly-force-rewrite
272 $ cd append-mostly-force-rewrite
273 $ current_uid=$(find_dirstate_uuid)
274
275 Change mtime of dir on disk which will be recorded, causing a small enough change
276 to warrant only an append, but we force the rewrite
277
278 $ touch -t 202212010000 dir2
279 $ hg st \
280 > --config rhg.on-unsupported=abort \
281 > --config devel.dirstate.v2.data_update_mode=force-new
282
283 UUID has changed and zero unused bytes means a full-rewrite happened
284
285
286 #if no-rust no-rhg
287 $ dirstate_uuid_has_not_changed
288 not testing because using Python implementation
289 #else
290 $ dirstate_uuid_has_not_changed
291 [1]
292 #endif
293 $ hg debugstate --docket | grep unused
294 number of unused bytes: 0
295 $ cd ..
296
297
298 Check the same scenario with the option set to "force-append"
299 -------------------------------------------------------------
300
301 (should behave the same as "auto" here)
302
303 $ cp -a force-base append-mostly-force-append
304 $ cd append-mostly-force-append
305 $ current_uid=$(find_dirstate_uuid)
306
307 Change mtime of dir on disk which will be recorded, causing a small enough change
308 to warrant only an append, which we are forcing here anyway.
309
310 $ touch -t 202212010000 dir2
311 $ hg st \
312 > --config rhg.on-unsupported=abort \
313 > --config devel.dirstate.v2.data_update_mode=force-append
314
315 UUID has not changed and some unused bytes exist in the data file
316
317 $ dirstate_uuid_has_not_changed
318 not testing because using Python implementation (no-rust no-rhg !)
319
320 #if no-rust no-rhg
321 The pure python implementation never appends at the time this is written.
322 $ hg debugstate --docket | grep unused
323 number of unused bytes: 0 (known-bad-output !)
324 #else
325 $ hg debugstate --docket | grep unused
326 number of unused bytes: [1-9]\d* (re)
327 #endif
328 $ cd ..
329
330 Check with the option in "auto" mode
331 ------------------------------------
332 $ cp -a force-base append-mostly-no-force-rewrite
333 $ cd append-mostly-no-force-rewrite
334 $ current_uid=$(find_dirstate_uuid)
335
336 Change mtime of everything on disk causing a full rewrite
337
338 $ touch -t 202212010005 `hg files`
339 $ hg st \
340 > --config rhg.on-unsupported=abort \
341 > --config devel.dirstate.v2.data_update_mode=auto
342
343 UUID has changed and zero unused bytes means we've rewritten.
344
345 #if no-rust no-rhg
346 $ dirstate_uuid_has_not_changed
347 not testing because using Python implementation
348 #else
349 $ dirstate_uuid_has_not_changed
350 [1]
351 #endif
352
353 $ hg debugstate --docket | grep unused
354 number of unused bytes: 0 (known-bad-output !)
355 $ cd ..
356
357 Check the same scenario with the option set to "force-new"
358 ---------------------------------------------------------
359
360 (should be the same as auto)
361
362 $ cp -a force-base append-mostly-force-rewrite
363 $ cd append-mostly-force-rewrite
364 $ current_uid=$(find_dirstate_uuid)
365
366 Change mtime of everything on disk causing a full rewrite
367
368 $ touch -t 202212010005 `hg files`
369 $ hg st \
370 > --config rhg.on-unsupported=abort \
371 > --config devel.dirstate.v2.data_update_mode=force-new
372
373 UUID has changed and a zero number unused bytes means we've rewritten.
374
375
376 #if no-rust no-rhg
377 $ dirstate_uuid_has_not_changed
378 not testing because using Python implementation
379 #else
380 $ dirstate_uuid_has_not_changed
381 [1]
382 #endif
383 $ hg debugstate --docket | grep unused
384 number of unused bytes: 0
385 $ cd ..
386
387
388 Check the same scenario with the option set to "force-append"
389 -------------------------------------------------------------
390
391 Should append even if "auto" did not
392
393 $ cp -a force-base append-mostly-force-append
394 $ cd append-mostly-force-append
395 $ current_uid=$(find_dirstate_uuid)
396
397 Change mtime of everything on disk causing a full rewrite
398
399 $ touch -t 202212010005 `hg files`
400 $ hg st \
401 > --config rhg.on-unsupported=abort \
402 > --config devel.dirstate.v2.data_update_mode=force-append
403
404 UUID has not changed and some unused bytes exist in the data file
405
406 $ dirstate_uuid_has_not_changed
407 not testing because using Python implementation (no-rust no-rhg !)
408
409 #if no-rust no-rhg
410 The pure python implementation is never appending at the time this is written.
411 $ hg debugstate --docket | grep unused
412 number of unused bytes: 0 (known-bad-output !)
413 #else
414 $ hg debugstate --docket | grep unused
415 number of unused bytes: [1-9]\d* (re)
416 #endif
417 $ cd ..
418
419
420
421 Get back into a state suitable for the test of the file.
422
423 $ cd ./append-mostly
424
425 #else
426 $ cd ./u
427 #endif
428
214 429 Transaction compatibility
215 -------------------------
430 =========================
216 431
217 432 The transaction preserves the dirstate.
218 433 We should make sure all of it (docket + data) is preserved
General Comments 0
You need to be logged in to leave comments. Login now