write files cache to cache directory
(self, files)
| 574 | return files |
| 575 | |
| 576 | def _write_files_cache(self, files): |
| 577 | """write files cache to cache directory""" |
| 578 | max_time_ns = 2**63 - 1 # nanoseconds, good until y2262 |
| 579 | # _self._newest_cmtime might be None if it was never set because no files were modified/added. |
| 580 | newest_cmtime = self._newest_cmtime if self._newest_cmtime is not None else max_time_ns |
| 581 | start_backup_time = self.start_backup - TIME_DIFFERS2_NS if self.start_backup is not None else max_time_ns |
| 582 | # we don't want to persist files cache entries of potentially problematic files: |
| 583 | discard_after = min(newest_cmtime, start_backup_time) |
| 584 | ttl = int(os.environ.get("BORG_FILES_CACHE_TTL", 2)) |
| 585 | files_cache_logger.debug("FILES-CACHE-SAVE: starting...") |
| 586 | cache_path = str(self.path / self.files_cache_name()) |
| 587 | with SaveFile(cache_path, binary=True) as sync_file: |
| 588 | with IntegrityCheckedFile(path=cache_path, write=True, override_fd=sync_file) as fd: |
| 589 | entries = 0 |
| 590 | age_discarded = 0 |
| 591 | race_discarded = 0 |
| 592 | for path_hash, entry in files.items(): |
| 593 | entry = self.decompress_entry(entry) |
| 594 | if entry.age == 0: # current entries |
| 595 | if max(timestamp_to_int(entry.ctime), timestamp_to_int(entry.mtime)) < discard_after: |
| 596 | # Only keep files seen in this backup that old enough not to suffer race conditions |
| 597 | # relating to filesystem snapshots and ctime/mtime granularity or being modified |
| 598 | # while we read them. |
| 599 | keep = True |
| 600 | else: |
| 601 | keep = False |
| 602 | race_discarded += 1 |
| 603 | else: # old entries |
| 604 | if entry.age < ttl: |
| 605 | # Also keep files from older backups that have not reached BORG_FILES_CACHE_TTL yet. |
| 606 | keep = True |
| 607 | else: |
| 608 | keep = False |
| 609 | age_discarded += 1 |
| 610 | if keep: |
| 611 | msgpack.pack((path_hash, entry), fd) |
| 612 | entries += 1 |
| 613 | integrity_data = fd.integrity_data |
| 614 | files_cache_logger.debug(f"FILES-CACHE-KILL: removed {age_discarded} entries with age >= TTL [{ttl}]") |
| 615 | t_str = datetime.fromtimestamp(discard_after / 1e9, timezone.utc).isoformat() |
| 616 | files_cache_logger.debug(f"FILES-CACHE-KILL: removed {race_discarded} entries with ctime/mtime >= {t_str}") |
| 617 | files_cache_logger.debug(f"FILES-CACHE-SAVE: finished, {entries} remaining entries saved.") |
| 618 | return integrity_data |
| 619 | |
| 620 | def file_known_and_unchanged(self, hashed_path, path_hash, st): |
| 621 | """ |
no test coverage detected