Nano Hash - криптовалюты, майнинг, программирование

Сбой при выполнении запроса collectionGroup в Cloud Firestore (Swift)

Я запускаю проект мобильного приложения с использованием Swift, SwiftUI и Cloud Firestore, где мне нужно найти пользователей на основе их различных настроек / предпочтений. Я решил это с помощью запроса collectionGroup. Но иногда (возможно, в 1 из 10 раз) запрос вылетает без какого-либо (для меня) понятного сообщения об ошибке. Составные индексы были созданы с использованием http-ссылок, предоставленных из XCode.

Я использую эту функцию:

func getUsersFromActivityPrefs(genders:[String], activities:[Int],skillScore_min:Int, skillScore_max:Int,completion:@escaping ([String]) -> ()) {
   
var matchUsers = [String]()
var count = 0
let db = Firestore.firestore()

for gender in genders {
    for activity in activities {
        let dbRef = db.collectionGroup("activity_preferences")
            .whereField("gender", isEqualTo: gender)
            .whereField("activityid", isEqualTo: activity)
            .whereField("status", isEqualTo: true)
            .whereField("skill_score", isGreaterThanOrEqualTo: skillScore_min)
            .whereField("skill_score", isLessThanOrEqualTo: skillScore_max)
            .limit(to: 100)
        dbRef.getDocuments {( snap, err) in
            count+=1
            if err != nil {
                print(err!.localizedDescription)
            }
            for i in snap!.documentChanges{
                let uid = i.document.get("uid") as? String ?? ""
                if uid != "" && !matchUsers.contains(uid) {
                    matchUsers.append(uid)
                    if matchUsers.count == 100 {
                        count = genders.count * activities.count
                        completion(matchUsers) //escaping completion handler
                        return
                    }
                }
            }
            if count == genders.count * activities.count {
                completion(matchUsers)
                return
            }
        }
    }
}

}

Я прикрепил журнал трассировки и сообщение о сбое от XCode. Я использую последнюю версию Firebase SDK, цель развертывания - iOS14. Это журнал трассировки, который я получаю:

thread # 1, queue = 'com.apple.main-thread', причина остановки = EXC_BAD_ACCESS (code = 1, address = 0x4f) frame # 0: 0x00007fff4b80dd66 AttributeGraphAG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>, AG::AttributeID, unsigned int, AGSwiftMetadata const*, bool*, long) + 322 frame #1: 0x00007fff4b81f1a5 AttributeGraphAGGraphGetValue + 203 frame # 2: 0x00007fff55e7ffab_SwiftUIift_SwiftUIift_ ›() + 294 кадр № 4: 0x00007fff55b9583a SwiftUIpartial apply forwarder for implicit closure #2 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in implicit closure #1 (A1.Type) -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<A1>) -> AttributeGraph.Attribute<A> in AttributeGraph.Attribute.init<A where A == A1.Value, A1: AttributeGraph.StatefulRule>(A1) -> AttributeGraph.Attribute<A> + 26 frame #5: 0x00007fff4b808d03 AttributeGraphAG :: Graph :: UpdateStack :: update () + 505 кадр № 6: 0x00007fff4b809199 AttributeGraphAG::Graph::update_attribute(AG::data::ptr<AG::Node>, bool) + 335 frame #7: 0x00007fff4b80d8e8 AttributeGraphAG :: Graph :: value_ref (AG :: AttributeID, AGSwiftMetadata *, bool 130 кадр № 8: 0x00007fff4b81f1f3 AttributeGraphAGGraphGetValue + 281 frame #9: 0x00007fff561aeeb7 SwiftUISwiftUI.GraphHost.updatePreferences () - ›Swift.Bool + 39 кадр № 10: 0x00007fff55c9a8cf SwiftUISwiftUI.ViewGraph.updateOutputs(at: SwiftUI.Time) -> () + 95 frame #11: 0x00007fff5611310c SwiftUIclosure # 1 () -› () в (расширение в SwiftIost. .Double, updateDisplayList: Swift.Bool) - ›() + 1308 кадр # 12: 0x00007fff56112327 SwiftUI(extension in SwiftUI):SwiftUI.ViewRendererHost.render(interval: Swift.Double, updateDisplayList: Swift.Bool) -> () + 343 frame #13: 0x00007fff55ba07de SwiftUIclosure # 1 () -› () в SwiftUI. 12 кадр # 16: 0x0000000112ebea88 libdispatch.dylib_dispatch_client_callout + 8 frame #17: 0x0000000112eccf23 libdispatch.dylib_dispatch_ma in_queue_callback_4CF + 1152 кадр № 18: 0x00007fff203a8276 CoreFoundation__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9 frame #19: 0x00007fff203a2b06 CoreFoundation__CFRunLoopRun + 2685 кадр № 20: 0x00007fff203a1b9e CoreFoundationCFRunLoopRunSpecific + 567 frame #21: 0x00007fff2b773db3 GraphicsServicesGSEventRunModal + 139000 кадр # 22: 0x00007103b1b_art_freepik_f2_c0000_i_fxf246

Вложения: сбой в XCode 1 Составной индекс 2


  • Похоже, вы используете SwiftUI? Похоже, это связано с ошибкой, а не с кодом вопроса. Кроме того, вообще говоря, Firebase не любит работать в тесных циклах - у вас их два, так что там может что-то быть. Вы можете подумать о том, чтобы изолировать проблему немного дальше, поскольку она является прерывистой - добавьте несколько операторов печати и / или разрывов и пошагово просматривайте код до тех пор, пока он не выйдет из строя, чтобы попытаться сузить проблему. 21.10.2020
  • Спасибо, Джей. Я изолировал сбой до строки getDocuments. Т.е. он никогда не попадает в обратную крышку. Есть ли способ избежать двух петель? Использование операторов whereField в Firebase очень ограничено, когда они находятся только в комбинациях (например, OR). 21.10.2020
  • @Jay, можете ли вы указать на что-нибудь, в котором говорится, что сетевые операции не зацикливаются? 21.10.2020
  • @bsod Наверное, нет, потому что это не то, что было заявлено :-) Вызовы пользовательского интерфейса в закрытии Firebase находятся в основном потоке, и это похоже на SwiftUI. Мы не знаем, откуда это было вызвано и как это влияет на пользовательский интерфейс, но похоже, что это связано со SwiftUI. Взгляните на этот ответ вместе с ответ. Все догадки, банкомат. 22.10.2020
  • @Jay это касается RTDB. Firestore - это другое животное, и я не видел никаких проблем с зацикливанием сетевых операций с ним, особенно с небольшим количеством, которое делает OP. Ошибка плохого доступа предполагает, что закрытие обращается к чему-то, что было преждевременно освобождено, или само закрытие отсутствует. Мне не удалось воспроизвести ошибку, поэтому я думаю, что вы правы в том, что проблема не в этом коде. 22.10.2020
  • @bsod, я не понимаю. Если ошибка находится за пределами этого кода, я смогу остановить выполнение на строке завершения (matchUsers), если у меня там есть точка останова? Я добавил команды печати перед строкой dbRef.getDocuments и в первой строке внутри замыкания (count + = 1), и сбой всегда происходит до достижения count + = 1, но после установки dbRef. Пожалуйста, дополните. 22.10.2020
  • @bsod Я согласен, это RTDB, но я ничего не сказал о зацикливании сетевых операций - это просто, поскольку все они выполняются в фоновых потоках. Обычно проблема заключается в пользовательском интерфейсе, поскольку жесткие циклы не дают замыканиям достаточно времени, чтобы обновить пользовательский интерфейс, не вызывая заиканий или других странных вещей - как в этом случае. Я не говорю, что это этот код, это может быть, но странно, что закрытие firebase не вызывается. 22.10.2020
  • Этот код не должен давать сбоев. Можете ли вы обновить свой вопрос с помощью своего текущего кода и сообщить нам, если это тот же сбой? 22.10.2020

Ответы:


1
  • Кажется, этот код работает нормально :): Так связана ли проблема с тем, что у меня есть два возврата ()? 22.10.2020
  • @Nikodym, а что будет, когда вы запустите это? 22.10.2020
  • Я получаю успешные отпечатки 22.10.2020
  • К сожалению, я получаю ту же ошибку сбоя с вашим измененным кодом. 22.10.2020
  • @Nikodym, тогда ваша проблема в том, как вы справляетесь с завершением, и я подозреваю, что это конкурирующие вызовы. Исправление простое, но я не могу сказать по вашему коду, что именно вы хотите. Вы ограничиваете свой запрос 100 результатами, но имеете дело только с изменениями документа (почему?) И проверяете, равно ли счетчик массива 100 (почему?). Каков план с этим захватом документа? 22.10.2020
  • На самом деле меня интересуют все документы, поэтому я думаю, мне следует изменить это на для документа в оснастке! .Documents {}. Причина проверки счетчика массива заключается в том, что запрос может возвращать одного и того же пользователя в разных циклах, поэтому мне нужно подсчитать уникальных пользователей, полученных во всех циклах. Но я попробую переместить второе завершение внутри цикла for: 22.10.2020
  • @Nikodym Я сделал еще одну правку. Я бы предложил это в качестве вашего шаблона. Получите документы, обработайте их, а затем вызовите завершение один раз после того, как вся работа будет завершена. Кроме того, вам следует подумать о том, чтобы всегда вызывать завершение, даже если моментальный снимок не был возвращен и произошла ошибка - верните nil, если нужно, просто верните что-нибудь (возможно, рассмотрите возможность возврата объекта Result). Вызывающий (этой функции) всегда должен ожидать какой-то ответ, чтобы общий поток работы всегда продолжался. 22.10.2020
  • @Nikodym, что вы можете сделать, что может быть лучшим решением, - это запустить оба цикла, а затем вызвать завершение один раз после того, как они оба закончили. Кроме того, если вас беспокоят повторяющиеся пользователи, вы можете перейти с контейнеров массива на контейнеры набора. Но если вы сначала просто запустите оба запроса, а затем объедините массивы вместе (отфильтровывая дубликаты), вы можете выполнить завершение один раз и значительно упростить ситуацию. 22.10.2020
  • спасибо за помощь. Но я не понимаю, как ваше предложение о том, что оператор завершения (matchUsers) внутри первого цикла for может вернуть все документы из двух циклов. Не исчезнет ли функция, как только завершение будет вызвано в первый раз в циклах, и тем самым упустить оставшиеся документы из других циклов? 22.10.2020
  • @Nikodym Я обновил свой ответ, чтобы отразить свое предложение. Используя группу диспетчеризации, вы можете сделать любое количество сетевых запросов и дать им единый обработчик завершения. 22.10.2020
  • Да, это хорошо. Но мне нужно изменить его, поэтому я выхожу, когда счетчик массива достигает 100. Но теперь я определил, почему приложение упало. Как вы сказали, это было вне этого кода. Но все же несколько странно, что он может аварийно завершить работу до того, как обработчик завершения выполнит 22.10.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..