Fix "Quota exceed error" in CloudKit and view container usage - ios

I have an app that uses CloudKit for data synchronisation.
With a larger volume of users, the data synchronisation between devices stopped working with an error:
<CKError 0x600001bbaca0: "Partial Failure" (2/1011); "Failed to modify some records"; uuid = 6D5A9621-7F98-4813-B7FC-1AE08E0AD16B;
container ID = "iCloud.EvenyCloudKit";
partial errors: {
3587C9C7-9E2C-460B-855B-C921EC4BDA3A:(com.apple.coredata.cloudkit.zone:__defaultOwner__) =
<CKError 0x600001bbac40: "Quota Exceeded" (25/2035);
server message = "Quota exceeded"; uuid = 6D5A9621-7F98-4813-B7FC-1AE08E0AD16B;
Retry after 347.0 seconds>
}>
I deployed my CloudKit schema to production, but it didn't fix the problem.
I have about 800 active users, and only small text data are stored in CloudKit.
How can I fix this problem, and how can I see the current CloudKit Container usage?
Thanks!

Related

Timeout errors which happens sometimes on Azure SQL Databases called in Azure Web jobs

We have a web application running on Azure that performs miscellaneous database maintenance tasks like creating databases, deleting unused databases, and so on. Everything is running on Azure SQL.
This application runs 24/24, and the maintenance tasks are performed every hour. Most of the time, everyhing goes well. However, the task sometimes ends up with errors like those ones :
HTTP error GatewayTimeout : The gateway did not receive a response from ‘Microsoft.Sql’ within the specified time period
HTTP error ServiceUnavailable : The request timed out
SQLException : Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
SQLException : A connection was successfully established with the server, but then an error occurred during the pre-login handshake
It seems like the database is not reachable when this happens.
We'd be glad if someone could help us to debug the issue.
thank you in advance.
There are transient errors and other type of errors that are particular to Azure SQL Database. Transient fault errors typically manifest as one of the following error messages from your client programs:
•Database on server is not currently available. Please retry the connection later. If the problem persists, contact customer support, and provide them the session tracing ID of
•Database on server is not currently available. Please retry the connection later. If the problem persists, contact customer support, and provide them the session tracing ID of . (Microsoft SQL Server, Error: 40613)
•An existing connection was forcibly closed by the remote host.
•System.Data.Entity.Core.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. ---> System.Data.SqlClient.SqlException: A transport-level error has occurred when receiving results from the server. (provider: Session Provider, error: 19 - Physical connection is not usable)
•An connection attempt to a secondary database failed because the database is in the process of reconfguration and it is busy applying new pages while in the middle of an active transation on the primary database.
Because of those errors and more explained here. It is necessary to create a retry logic on applications that connect to Azure SQL Database.
public void HandleTransients()
{
var connStr = "some database";
var _policy = RetryPolicy.Create < SqlAzureTransientErrorDetectionStrategy(
retryCount: 3,
retryInterval: TimeSpan.FromSeconds(5));
using (var conn = new ReliableSqlConnection(connStr, _policy))
{
// Do SQL stuff here.
}
}
More about how to create a retry logic here.
Throttling is also a cause of timeouts. The following queries may help you understand the impact of workloads on the Azure SQL database.
SELECT
(COUNT(end_time) - SUM(CASE WHEN avg_cpu_percent > 80 THEN 1 ELSE 0 END) * 1.0) / COUNT(end_time) AS 'CPU Fit Percent'
,(COUNT(end_time) - SUM(CASE WHEN avg_log_write_percent > 80 THEN 1 ELSE 0 END) * 1.0) / COUNT(end_time) AS 'Log Write Fit Percent'
,(COUNT(end_time) - SUM(CASE WHEN avg_data_io_percent > 80 THEN 1 ELSE 0 END) * 1.0) / COUNT(end_time) AS 'Physical Data Read Fit Percent'
FROM sys.dm_db_resource_stats
--service level objective (SLO) of 99.9% <= go to next tier

CloudKit: "There is no operation associated with this request"

When attempting to save a CloudKit CKRecord, I get a CKError returned which gives the following description:
<CKError 0x7fd3d24e1810: "Internal Error" (1/2005); "there is no operation
associated with this request">
Any ideas what might cause this? Google does not return any results at all to this error.
This error is mentioned in the CloudKit constants reference here. Clearly this is some sort of internal issue in CK. Is the issue recurring? Are you doing anything special in this operation?

"Server Rejected Request" (15/2001); "Request failed with http status code 500"

fetchUserRecordIDWithCompletionHandler returns:
<CKError 0x14daad30: "Server Rejected Request" (15/2001); "Request failed with http status code 500">
I have never seen this error with CloudKit. Do you think it is associated that some iCloud service was down nowadays?
defaultContainer.fetchUserRecordIDWithCompletionHandler({ _userRecordID, error in
if error == nil {
userRecordID = _userRecordID
loggedInUserRecordName = _userRecordID.recordName
dispatch_async(dispatch_get_main_queue(), {
self.progressView.setProgress(2 / self.steps, animated: true)
})
} else {
Utility.log("error 1231: \(error.localizedDescription)")
}
dispatch_semaphore_signal(self.sema)
})
Strange that fetchUserRecordIDWithCompletionHandler works in one of my other project with an other container, but usually does not work with this project with this container.
Any idea way?
Probably a server issue at the other end. 500 error code is unexpected internal error at server.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
This can happen when you are using wrong container. In my project I was using custom container that did not match default app container. Switching to correct one with custom identifier solved the issue.
Check the id of the CKContainer in Capabilities and your CKContainer's initialization make ensure that they are the same. In my case, I make this mistake, and the error?.localizedDescription is Optional("Request failed with http status code 500")

CloudKit Queries for logged out users

So according to Apple's documentation, users not logged into iCloud are still allowed to read from public databases, but when querying a public database, I get the following error:
Error Domain=NSCocoaErrorDomain Code=4097 "The operation couldn’t be completed. (Cocoa error 4097.)" (connection to service named com.apple.cloudd) UserInfo=0x7c3498c0 {NSDebugDescription=connection to service named com.apple.cloudd}
Here's my code for reference:
let container = CKContainer.defaultContainer()
let database = container.publicCloudDatabase
let predicate = NSPredicate(value: true)
let episodeQuery = CKQuery(recordType: "Episode", predicate: predicate)
database.performQuery(episodeQuery, inZoneWithID: nil) {
// Record handling goes here
}
Any thoughts, tips, or advice is certainly appreciated. Thanks so much
Error 4097 is returned when your application can't talk to cloudd, the CloudKit daemon.
This could either be due to an issue with entitlements or a bug in cloudd. Check your syslog for more clues and look for crash logs from cloudd. If you have a crash log for cloudd please attach it to a new radar at bugreport.apple.com
I had a similar issue - after checking the system log as suggested by #farktronix, after filtering by cloudd I saw:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unexpected expression: NSSelfExpression'
Turns out my query predicate was incorrect - in my subscription query, I was trying to use the predicate:
NSPredicate(format: "self = %#", someCKRecord)
whereas I should have been using
NSPredicate(format: "recordID = %#", someCKRecord.recordID)
I have the similar issue if I send too many times requests to iCloud.
It happens for me when I'm trying to do CKModifyRecordsOperation and there are more than 100 recordsToSave. I tried about 3 consecutive times to do this operation. And then I got this error. Then every single CloudKit API has been denied. After few minutes later, every recovered.
2019-01-14 20:27:19.326912+1300 inkDiary[27136:5424371] [LogFacilityCK] Got a connection error for operation 95C747D64BEA5FAC: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.cloudd" UserInfo={NSDebugDescription=connection to service named com.apple.cloudd}
Optional(Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.cloudd" UserInfo={NSDebugDescription=connection to service named com.apple.cloudd})

Get partial errors from NSError

When using CloudKit, sometimes the error returned is PartialFailure, which may caused by duplicate subscriptions, etc. See example below.
<CKError 0x7f8318711520: "Partial Failure" (2/1011);
"Failed to modify some subscriptions"; uuid = A434B010-7650-4BBA-8A7A-33CD0690FD15;
container ID = "iCloud.xxx.xxx"; partial errors: {
EFC65F4A-A595-44A3-A022-323D9CE9B535 = <CKError 0x7f831a007be0: "Server Rejected Request" (15/2032); server message = "subscription is duplicate of '_930081460_AA87A676-DE57-4530-8BB8-7465BF4F4303'">
C4913907-28F3-42DB-8455-9966D9084834 = <CKError 0x7f83185cfc20: "Server Rejected Request" (15/2032); server message = "subscription is duplicate of '_930081460_F92FA91D-3E92-4E46-AE59-E912F8871026'"> }>
I wish to retrieve these partial errors from the main error object but I don't know how. NSError doesn't have a partialError property, and it neither has a key in userInfo to retrieve that.
You aren't getting back a straight NSError, you are getting back a CKError. Looking at the documentation for CKError, there is in fact a CKPartialErrorsByItemIDKey key. That looks like the key that would return a dictionary of CKErrors keyed by item ID if you ask me! The userInfo object should contain that key.
Also documented here
CloudKit Framework Constants Reference
Two years later, and I still don't think how to access partial errors is particularly well documented. Thanks to #Acey for putting me on the right path with CKPartialErrorsByItemIDKey
For those who are struggling, here is an example of how I ended up accessing a partial error in a CKModifySubscriptionsOperation (Swift 2.2):
someZoneSubscriptionOperation.modifySubscriptionsCompletionBlock = {(savedSubscriptions: [CKSubscription]?, deletedSubscriptionIDs:[String]?, operationError:NSError?) in
// check specifically for an error in changing subscriptions to custom zone with specific subscriptionID
if let partialError = operationError?.userInfo[CKPartialErrorsByItemIDKey]?[subscriptionID] as? ErrorType {
print(partialError) // prints partial error for custom zone with `subscriptionID` if it exists
}
}

Resources