CKError: Фильтр запросов превышает предел значений: 250 для контейнера
Я хочу собрать около 500 записей "Визит" из публичной базы данных. CloudKit дает вам только 100 записей за раз, поэтому я просто использую CKQueryCursor, как показано ниже, чтобы получить все записи, которые я хочу.
func fetchVisits(_ cursor: CKQueryCursor? = nil) {
print("fetchVisits \(cursor)")
var operation: CKQueryOperation!
if let cursor = cursor {
operation = CKQueryOperation(cursor: cursor)
} else {
let query = CKQuery(recordType: "Visit", predicate: NSPredicate(format: "Client IN %@ AND ANY Students IN %@", visitClients, visitStudents))
operation = CKQueryOperation(query: query)
}
operation.recordFetchedBlock = {
(record) in
totalVisits.append(record)
}
operation.queryCompletionBlock = {
(cursor, error) in
if let error = error {
//handle error
} else if let cursor = cursor {
self.fetchVisits(cursor)
} else {
//all done!
}
}
CKContainer.default().publicCloudDatabase.add(operation)
}
Я называю функцию как:
fetchVisits()
Это прекрасно работает, он получает все посещения, которые мне нужны. Консольный журнал
fetchVisits nil
fetchVisits Optional(<CKQueryCursor: 0x174228140; id=4bb7887c326fc719, zone=(null)>)
fetchVisits Optional(<CKQueryCursor: 0x17422a320; id=f67fb25669486da9, zone=(null)>)
fetchVisits Optional(<CKQueryCursor: 0x174228380; id=7e87eb8b7cfe1a74, zone=(null)>)
fetchVisits Optional(<CKQueryCursor: 0x17422cc80; id=e77e47ef2b29c8a4, zone=(null)>)
Но проблема в том, что теперь я хочу обновить, когда я нажимаю кнопку, и теперь она дает мне эту ошибку:
"Сервис недоступен" (6/2022); "Ошибка запроса с кодом состояния http 503"; Повторите попытку через 30 секунд
Что само собой разумеется, я думаю, я перегружаю сервер, запрашивая жалкие 500 записей? Поэтому я жду 30 секунд и снова вызываю функцию, и теперь я получаю эту ошибку.
"Превышен лимит" (27/2023); server message = "Фильтр запросов превышает предел значений: 250 для контейнера
По какой-то причине я не могу запустить эту функцию снова. Если я перезапущу приложение, оно снова будет работать нормально, но только в первый раз. Эта проблема характерна для любой таблицы, которая возвращает CKQueryCursor. У меня есть другие таблицы, в которые я попал, которые содержат менее 100 записей (поэтому курсор равен нулю), и я могу вытащить их несколько раз без каких-либо проблем.
2 ответа
Итак, я видел это раньше, и проблема, я полагаю, заключается в ошибке на серверах CloudKit. По моему опыту, это связано со сложными запросами.
Если вы попытаетесь изменить свой предикат на:
NSPredicate(value: true)
Или даже упростить существующую, удалив ЛЮБУЮ деталь, этого может быть достаточно, чтобы исправить это.
Вы запрашиваете больше CKRecords для iCloud до завершения операции запроса.
...
operation.queryCompletionBlock = {
(cursor, error) in
if let error = error {
//handle error
} else if let cursor = cursor {
self.fetchVisits(cursor)
} else {
//all done!
}
}
...
функция self.fetchVisits(cursor)
вызов выполняется внутри блока завершения, это означает, что вы запрашиваете больше записей до завершения текущей операции.
Возможное решение состоит в том, чтобы использовать замыкание (completeHandler), в которое вы включаете CKQueryCursor, когда пользователю нужно больше записей (прокрутка таблицы или что-то еще), вы вызываете один раз self.fetchVisits
прохождение курсора получено на ваше закрытие.
РЕДАКТИРОВАТЬ: Я думаю, что к настоящему времени я нашел причину:
Хотя мой предыдущий ответ (ниже) верен, он не относится к этой проблеме.
К настоящему времени я столкнулся с двумя ошибками сервера, упомянутыми в вопросе, и исследовал ситуацию.
Моя установка следующая:
я хочу загрузить подмножество записей iCloud в зависимости от условия, указанного в предикате:
let doNotDownloadIfOlderFormat = "NOT " + iCloudRecordKeyName + " IN %@"
let doNotDownloadIfOlderPredicate = NSPredicate.init(format: doNotDownloadIfOlderFormat, doNotDownloadIfOlder)
Вот, doNotDownloadIfOlder
представляет собой набор настраиваемых объектов.
В некоторых тестовых случаях их количество превышало 300. Я подозревал, что это могло быть причиной сообщения об ошибке. Фильтр запросов превышает лимит значений: 250 для контейнера.
Таким образом, я изменил свой код:
let doNotDownloadIfOlderFormat = "NOT " + iCloudRecordKeyName + " IN %@"
let limit = min(doNotDownloadIfOlder.count, max)
let shortened = Set(Array(doNotDownloadIfOlder)[0 ..< limit])
let doNotDownloadIfOlderPredicate = NSPredicate.init(format: doNotDownloadIfOlderFormat, shortened)
где я установил max
до тестового значения ниже 300.
Вот результат:
- max <= 140: без ошибок
- 141 <= max <= 250: "Служба недоступна" (6/2022); "Запрос не удался, код статуса http 503"; Повторить попытку через 30,0 секунд>
- max > 250: "Превышен предел" (27/2023); server message = "Фильтр запроса превышает лимит значений: 250 для контейнера
Очевидно, существует недокументированный лимит сервера для запросов. Странное число 140 могло иметь какое-то отношение к размеру моих пользовательских объектов.
Предыдущий ответ:
В документах говорится:
Сервер может изменить свои ограничения в любое время, но следующие общие рекомендации:
- 400 элементов (записей или акций) на операцию
- 2 МБ на запрос (не считая размеров активов)
Если ваше приложение получает
CKError.Code.limitExceeded
, он должен разделить операцию пополам и повторить оба запроса.