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 = |
|
|
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 |
|
|
|
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