If macOS does one thing most of all, it caches. It works on the basis that it has limitless local storage to accommodate as much cached data as it wishes, so accelerating many of its tasks. Such extensive caching poses problems when free disk space is low. macOS therefore has a space management system that can purge caches when additional free space is needed. This article is a first preliminary look at how space management works in macOS Ventura 13.3.1 running on Apple silicon. It’s based on log records obtained for one intentional purge performed during my recent demonstration of the system in action, and its flaws.
What is purgeable?
Because of the complexity of the space management system, and its changes over the years, there is confusion over exactly what can be counted as purgeable. Judging by what is recorded in the log during a purge, the current list includes the following:
- volume caches,
- subsystem caches, including QuickLook Thumbnails, Asset Cache (Content Caching), Mail caches,
- specific service caches, app extension caches, plug-ins,
- Time Machine snapshots.
Apple is correct in stating that the user has no control over those, except for the last, TM snapshots, which can of course be deleted by the user.
Any eviction of iCloud Drive files or other iCloud items is dependent on the user enabling the Optimise Mac Storage option in iCloud settings. Although that was enabled here, as the volume being purged contained no iCloud items, that couldn’t be observed.
Snapshots are particularly confusing: because purging them is the task of Time Machine, only Time Machine snapshots are purgeable, and not those made by other processes, including third-party backup utilities. This is complicated by Time Machine’s automatic purging of snapshots that are more than 24 hours old; old snapshots on volumes that are excluded from Time Machine backups aren’t purged automatically when backing up, but can still be purged on demand by CacheDelete.
How are they purged?
Times in log extracts below are given in seconds elapsed from a start at 0.068774 seconds.
When you drag a file to copy it to another volume, the Finder calls on File Coordination (com.apple.foundation.filecoordination) to perform that action. It sets up a server and client for the purpose; if the copy can’t take place using existing free space, this is passed to CacheDelete (com.apple.cache_delete) to free up sufficient purgeable space to complete that copy action.
The process of purging that follows is performed in a series of phases, proceeding in the order given in the list above, with snapshots last. Once any given phase has succeeded in purging sufficient space for the copy to complete successfully, purging should cease.
The first phase consists of a series of attempts to delete volume caches at a level of urgency of 3. Following those, individual subsystems are polled to see if they can delete sufficient cache. Those include: photolibraryd, osanalytics.user, quicklook Thumbnails, cloudd, triald, video, container caches, Time Machine snapshots, mobileassetd, memory exception reports, powerlog, Spotlight metadata, iBooks, geod, commerce, AssetCache (Content Caching), bird (iCloud), Neural Engine, Mail, replayd, movie, app extensions, music, imagent, File Provider, logd, Safari, desktoppicture, system installd, podcast, and nsurlsession. Note that this concerns caches, rather than all stored content of those subsystems.
APFS then handles lists of individual requests to purge those files at an urgency of 1. At the end of that, it reports how many files were purged, and the total space recovered:
0.146019 apfs handle_purge_files:10874: Purged 0 bytes in 0 files
reported back to the deleted helper as the file system purge, fsPurge:
0.146070 deleted_helper fsPurge: amountPurged 0 postPurgeFreespace 6158180352 prePurgeFreespace 6158180352
0.146072 deleted_helper fsPurge: 0 at urgency 1 (flags: 0x30) on /Volumes/TestVolume1 (/Volumes/TestVolume1)
Surprisingly, the same processes are repeated for other volumes in the same container, reporting
0.149966 deleted_helper fsPurge: amountPurged 0 postPurgeFreespace 6158180352 prePurgeFreespace 6158180352
0.149968 deleted_helper fsPurge: 0 at urgency 1 (flags: 0x30) on /Volumes/TestVolume2 (/Volumes/TestVolume2)
Those are repeated at a level of urgency of 2, then 3, with flags respectively 0x50 and 0x90. The overall result is then declared:
0.263120 Error CacheDelete was unable to satisfy the purge request for volume: /Volumes/TestVolume1
0.263921 Purge Result: 0 bytes on: <CacheDeleteDaemonVolume> at: /Volumes/TestVolume1 [/dev/disk9s1 : apfs] freespace: 6158180352 (6.16 GB), initialFreespace: 6158180352 (6.16 GB), used: 94000992256 (94.00 GB), total size: 100284338176 (100.28 GB)PRIMARY, diskRef: 0x145e41e10
deleted then gives summary figures, including elapsed time, free space at the end, and the result, in this case that no bytes were freed.
A round of further purge attempts is aimed at specific service caches, including replayd, cloudd, bird, app extension services (IDECacheDeleteAppExtension) and plug-ins.
Only when all attempts to purge caches have failed to return the required space does CacheDelete turn to snapshots. It then asks TimeMachine to perform space-based snapshot purging:
1.212973 TimeMachine Received request to purge space with urgency: 1, info: […]
1.227519 TimeMachine Starting space-based purging of local snapshots on disk '/Volumes/TestVolume1' - current free space: 6.16 GB (6,158,180,352 bytes), target free space: 10 GB (10,000,003,072 bytes), initial free space: 6.16 GB (6,158,180,352 bytes), urgency: 1
1.227705 TimeMachine Starting age based thinning of Time Machine local snapshots on disk '/Volumes/TestVolume1'
and APFS deletes the data and file system for that snapshot:
1.233367 apfs apfs_snap_vnop_remove:1052: disk9s1 proc backupd: deleting snapshot name (com.apple.TimeMachine.2023-04-15-212139.local) w/xid 99
1.233617 apfs cleanup_snapshot_purgatory:1808: disk9s1 processing snap xid 99...
1.233651 apfs delete_clone_fs:2778: disk9s1 forward merge, src_snap_xid 99, dest_snap_xid 0, cur_tree_count 73, next_tree_count 19
With purgeable space finally purged and available for use, the file copy is completed.
The purging detailed here resulted from an attempt to copy a 10 GB file to TestVolume1, and succeeded because the very large and blocking TM snapshot on that volume was purged. A previous attempt to copy the same 10 GB file to TestVolume2, which had only a very small snapshot, was rejected as macOS deemed that it was unable to purge sufficient space to accommodate that. This demonstrates that, while some of the earlier purging of caches was applied to both volumes, CacheDelete didn’t ask Time Machine to delete the snapshot on TestVolume1, although that would have freed sufficient space for the copy to have proceeded.
Throughout log extracts of this demonstration, CacheDelete worked with three different figures for purgeable space: one for each of the two volumes, which differed greatly because of the snapshot, and one for the APFS container, which might represent the total for the two volumes except for the snapshots. I will explore this in a later article in which I will examine CacheDelete’s measurements across the different stages of the demonstration.
CacheDelete appears to take little or no notice of APFS Volume Roles. In another log extract, it attempted to obtain purgeable space on the VM volume in the active Boot Volume Group! In this case, the great majority of its measurements of purgeable space were directed at caches only found on paired Data volumes, when the two volumes being assessed had no assigned Volume Roles.
Purging shown here was far simpler than that expected when purging an active Data volume, where macOS caches are usually in more extensive use, and some files are held in iCloud. They may also differ between Intel and Apple silicon Macs. However, the principles seen here should apply more widely, and are at least a start in understanding the behaviour of space management in macOS. I have a lot more log extracts to analyse yet.