CloudKit Throttles and Debugging
The CloudKit infrastructure is shared by all apps and services. The resources are finite, and so high utilization from one app can negatively affect others. To avoid this kind of impact and optimize the overall experience, CloudKit implements a number of limits and controls on incoming traffic, which are known as throttles.
CloudKit can enforce throttles when it deems necessary on any app or service that uses the CloudKit framework, CloudKit Web Services, CloudKit JS, NSPersistentCloudKitContainer, and NSUbiquitousKeyValueStore. This technote discusses how to identify CloudKit throttles with representative error messages and how to handle them.
It does not actually say what the limits are.
I came to suspect that iCloud imposed quotas on its use nearly six years ago, when I was exploring the only command tool that provides any useful information about iCloud,
brctl
. When examining one of its dumps, I came across an entry forsyncUpBudget
referring to BRCSyncBudgetThrottle, and another itemglobal sync up budget
giving the budget available. As with almost everything inbrctl
and iCloud generally, there appeared to be no documentation of these.[…]
Devices can also apply their own local system throttles in some circumstances; for example, when the device’s battery runs low, its system may well throttle CloudKit requests until the device has been recharged to a specific battery level. Those shouldn’t affect the syncing of other devices, though.
[…]
Perhaps the worst approach the user could then try is one of the solutions most commonly recommended: turning iCloud off and back on again, as it has no effect on the retry interval, and could trigger further throttling.
Apple has recently confirmed that CloudKit databases can be throttled, which effectively blocks all access to them for requests for a set period of time. This isn’t a limitation in transfer rate in the way that iCloud Drive might experience, but an intentional denial of service until the retry interval has elapsed.
[…]
Apple currently imposes limits on the number of items that can be stored in shared databases and elsewhere in iCloud. These are given here for Contacts, Calendars, Reminders, Bookmarks and Maps, here for mailboxes and message size, and here for Shared Albums.
Throttling, as described by Apple, doesn’t make any sense in the context of iCloud Drive, as CloudKit doesn’t manage that, and no app is making requests of CloudKit in the process.
But iCloud Drive is built on CloudKit, which as Apple says is shared infrastructure. It’s not clear to me whether CloudKit will throttle one app due to high utilization from another app or system service (iCloud Drive, iCloud Photos).
Stages in transfers with iCloud Drive are subject to throttling, although throttles appear to occur infrequently and only last a few hundred milliseconds.
[…]
For transfer and storage in iCloud, files are divided into chunks of just over 15,350 bytes in size, although the maximum chunk size imposed by the system is either 28,455,742 bytes (28 MB), or a fixed maximum of 33,554,432 bytes (33 MB).
Some iCloud servers may impose a
connection.max.requests
of 100, although others are unlimited.
Under the hood,
NSPersistentCloudKitContainer
separates the synchronization process into many tasks, and encapsulates all the implementation details. When performing a task, it generates logs, which are persisted as a part of a sysdiagnose. To understand what really happens in the process, which is sometimes necessary when diagnosing a synchronization issue, you need to look into a sysdiagnose.This technote unveils some details inside the synchronization by analyzing a sysdiagnose, and provides some representative logs that can be used to identify some important tasks and their state.
A synchronization failure can happen because of a code-level issue in your data presentation layer, a configuration issue related to CloudKit, or a limit on the system side. To debug a synchronization issue, look into the system logs in Xcode console or a sysdiagnose, then identify the relevant errors. This technote describes how to identify and resolve common errors seen in the logs when working with
NSPersistentCloudKitContainer
.
Yay! New weird CloudKit situation: The background notifications get delivered to the device, but not to the app unless it’s restarted
5 hours later and Photos.app is still stuck on “Syncing with iCloud”. I’ll let it run overnight but it’s not looking good.
Previously:
- Photos Syncing With iCloud Paused
- iCloud Syncing Limitations & Solutions
- NSPersistentCloudKitContainer in 2022
- Syncing Core Data With CloudKit and NSPersistentCloudKitContainer
4 Comments RSS · Twitter · Mastodon
Hopefully when they are throttling they give apps errors with that retry interval to display in the UI so it doesn’t just look like everything is broken to the user.
@ObjC4Life I guess you could do that if handling the error yourself, but it sounds like Apple’s recommendation (cf. CKSyncEngine) is to just transparently wait and retry.
I haven't looked into the new CKSyncEngine stuff yet. My CloudKit app still uses CKRecordZoneSubscription/CKFetchRecordZoneChangesOperation. I don't use any of the CoreData/iCloud magic either for the local cache I use a SQLite database and manage my change table and merging myself. I started writing this app before all these APIs existed but I think even if that weren't the case I'd still rather use SQLite over CoreData.
Very rarely, my app will get a retry error and I display the countdown in the UI (it doesn't happen very often to me) but it's nice to see what's going on when it happens. If CKSyncEngine just silently retries hopefully they still at least have some callbacks so developers can show something in the UI even if CKSyncEngine is nice enough to handle the error. IMO it isn't a great user experience for syncing not be working for ten minutes or something and not telling the user why.