##// 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 b'deprec-warn',
640 b'deprec-warn',
641 default=False,
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 coreconfigitem(
652 coreconfigitem(
644 b'devel',
653 b'devel',
645 b'disableloaddefaultcerts',
654 b'disableloaddefaultcerts',
@@ -33,6 +33,7 b' rangemask = 0x7FFFFFFF'
33
33
34 WRITE_MODE_AUTO = 0
34 WRITE_MODE_AUTO = 0
35 WRITE_MODE_FORCE_NEW = 1
35 WRITE_MODE_FORCE_NEW = 1
36 WRITE_MODE_FORCE_APPEND = 2
36
37
37
38
38 class _dirstatemapcommon:
39 class _dirstatemapcommon:
@@ -57,6 +58,16 b' class _dirstatemapcommon:'
57 self._parents = None
58 self._parents = None
58 self._dirtyparents = False
59 self._dirtyparents = False
59 self._docket = None
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 # for consistent view between _pl() and _read() invocations
72 # for consistent view between _pl() and _read() invocations
62 self._pendingmode = None
73 self._pendingmode = None
@@ -612,7 +623,7 b' if rustmod is not None:'
612 return
623 return
613
624
614 # We can only append to an existing data file if there is one
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 if self.docket.uuid is None:
627 if self.docket.uuid is None:
617 write_mode = WRITE_MODE_FORCE_NEW
628 write_mode = WRITE_MODE_FORCE_NEW
618 packed, meta, append = self._map.write_v2(write_mode)
629 packed, meta, append = self._map.write_v2(write_mode)
@@ -42,6 +42,7 b' pub enum DirstateVersion {'
42 pub enum DirstateMapWriteMode {
42 pub enum DirstateMapWriteMode {
43 Auto,
43 Auto,
44 ForceNewDataFile,
44 ForceNewDataFile,
45 ForceAppend,
45 }
46 }
46
47
47 #[derive(Debug)]
48 #[derive(Debug)]
@@ -69,6 +70,9 b" pub struct DirstateMap<'on_disk> {"
69 pub(super) old_data_size: usize,
70 pub(super) old_data_size: usize,
70
71
71 pub(super) dirstate_version: DirstateVersion,
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 /// Using a plain `HgPathBuf` of the full path from the repository root as a
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 unreachable_bytes: 0,
461 unreachable_bytes: 0,
458 old_data_size: 0,
462 old_data_size: 0,
459 dirstate_version: DirstateVersion::V1,
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 /// append to the existing data file that contains `self.on_disk` (true),
530 /// append to the existing data file that contains `self.on_disk` (true),
526 /// or create a new data file from scratch (false).
531 /// or create a new data file from scratch (false).
527 pub(super) fn write_should_append(&self) -> bool {
532 pub(super) fn write_should_append(&self) -> bool {
528 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
533 match self.write_mode {
529 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
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 fn get_node<'tree>(
544 fn get_node<'tree>(
@@ -923,6 +935,10 b" impl<'on_disk> DirstateMap<'on_disk> {"
923 *unreachable_bytes += path.len() as u32
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 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
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 unreachable_bytes: meta.unreachable_bytes.get(),
313 unreachable_bytes: meta.unreachable_bytes.get(),
314 old_data_size: on_disk.len(),
314 old_data_size: on_disk.len(),
315 dirstate_version: DirstateVersion::V2,
315 dirstate_version: DirstateVersion::V2,
316 write_mode: DirstateMapWriteMode::Auto,
316 };
317 };
317 Ok(dirstate_map)
318 Ok(dirstate_map)
318 }
319 }
@@ -641,6 +642,7 b' pub(super) fn write('
641 let append = match write_mode {
642 let append = match write_mode {
642 DirstateMapWriteMode::Auto => dirstate_map.write_should_append(),
643 DirstateMapWriteMode::Auto => dirstate_map.write_should_append(),
643 DirstateMapWriteMode::ForceNewDataFile => false,
644 DirstateMapWriteMode::ForceNewDataFile => false,
645 DirstateMapWriteMode::ForceAppend => true,
644 };
646 };
645 if append {
647 if append {
646 log::trace!("appending to the dirstate data file");
648 log::trace!("appending to the dirstate data file");
@@ -321,22 +321,39 b' impl Repo {'
321 .set(Some(docket.uuid.to_owned()));
321 .set(Some(docket.uuid.to_owned()));
322 let data_size = docket.data_size();
322 let data_size = docket.data_size();
323 let metadata = docket.tree_metadata();
323 let metadata = docket.tree_metadata();
324 if crate::vfs::is_on_nfs_mount(docket.data_filename()) {
324 let mut map =
325 // Don't mmap on NFS to prevent `SIGBUS` error on deletion
325 if crate::vfs::is_on_nfs_mount(docket.data_filename()) {
326 OwningDirstateMap::new_v2(
326 // Don't mmap on NFS to prevent `SIGBUS` error on deletion
327 self.hg_vfs().read(docket.data_filename())?,
327 OwningDirstateMap::new_v2(
328 data_size,
328 self.hg_vfs().read(docket.data_filename())?,
329 metadata,
329 data_size,
330 )
330 metadata,
331 } else if let Some(data_mmap) = self
331 )
332 .hg_vfs()
332 } else if let Some(data_mmap) = self
333 .mmap_open(docket.data_filename())
333 .hg_vfs()
334 .io_not_found_as_none()?
334 .mmap_open(docket.data_filename())
335 {
335 .io_not_found_as_none()?
336 OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
336 {
337 } else {
337 OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
338 OwningDirstateMap::new_v2(Vec::new(), data_size, metadata)
338 } else {
339 }
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 } else {
357 } else {
341 let (map, parents) =
358 let (map, parents) =
342 OwningDirstateMap::new_v1(dirstate_file_contents)?;
359 OwningDirstateMap::new_v1(dirstate_file_contents)?;
@@ -254,6 +254,7 b' py_class!(pub class DirstateMap |py| {'
254 let rust_write_mode = match write_mode {
254 let rust_write_mode = match write_mode {
255 0 => DirstateMapWriteMode::Auto,
255 0 => DirstateMapWriteMode::Auto,
256 1 => DirstateMapWriteMode::ForceNewDataFile,
256 1 => DirstateMapWriteMode::ForceNewDataFile,
257 2 => DirstateMapWriteMode::ForceAppend,
257 _ => DirstateMapWriteMode::Auto, // XXX should we error out?
258 _ => DirstateMapWriteMode::Auto, // XXX should we error out?
258 };
259 };
259 let result = inner.pack_v2(rust_write_mode);
260 let result = inner.pack_v2(rust_write_mode);
@@ -93,7 +93,7 b' Add some unknown files and refresh the d'
93 $ hg add dir/o
93 $ hg add dir/o
94 $ hg remove dir/nested/m
94 $ hg remove dir/nested/m
95
95
96 $ hg st
96 $ hg st --config devel.dirstate.v2.data_update_mode=force-new
97 A dir/o
97 A dir/o
98 R dir/nested/m
98 R dir/nested/m
99 ? dir/n
99 ? dir/n
@@ -211,8 +211,223 b' Check that unused bytes counter is reset'
211
211
212 #endif
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 Transaction compatibility
429 Transaction compatibility
215 -------------------------
430 =========================
216
431
217 The transaction preserves the dirstate.
432 The transaction preserves the dirstate.
218 We should make sure all of it (docket + data) is preserved
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