Как правильно предотвратить потерю данных для аналитических данных? - PullRequest
0 голосов
/ 11 марта 2020

Я сейчас пишу аналитическую систему. В настоящее время он кэширует события в оперативной памяти. Он записывает в файловую систему через NSUserDefaults (iOS) и SharedPreferences (Android), когда приложение закрывается, как JSON. Эти данные читаются при открытии приложения.

Он также отправляет каждые N секунд или когда количество событий достигает 20. Когда отправка прошла успешно, он удаляет все события, отправленные из ОЗУ.

Это имеет очевидный fl aws: при сбое приложения все данные за N секунд теряются. Когда не удается связаться с сервером (например, из-за того, что сервер не работает) и происходит сбой приложения, еще больше данных теряется.

Мой вопрос здесь такой: как я могу повысить «безопасность» своих данных и предотвратить массовое потеря данных, когда сервер недоступен или недоступен?

Вот мой текущий код (удалены неважные части)

import Foundation
class BackendTrackingHandler : TrackingHandler {
    static let KEY_CACHE_EVENT = "TrackingCache"
    private static let SEND_INTERVAL:TimeInterval = 10
    var cachedEvents: [TrackingEvent] = []
    var temporaryCachedEvents: [TrackingEvent] = []
    var prefix: String
    var endpoint: String
    var timer : Timer?
    //whether we currently wait for a response
    var isSending: Bool = false

override init() {
    //init
    readCachedEventsFromDisk()
    timer = Timer.scheduledTimer(timeInterval: BackendTrackingHandler.SEND_INTERVAL, target: self, selector: #selector(send), userInfo: nil, repeats: true)
}

    override func trackEvent(_ event: TrackingEvent) {
        cachedEvents.append(event)
        if((cachedEvents.count) >= 20) {
            send()
        }
    }

    @objc func send() {

        if((cachedEvents.count) < 1) {
            return
        }
        if(isSending) {
            return
        }
        isSending = true
        let enc = JSONEncoder()
        enc.outputFormatting = .prettyPrinted
        let data = try! enc.encode(cachedEvents)
        // Constructring Request here
        let session = URLSession.shared
        //while the request is on the way, we can trigger new events. Make a temporary copy
        temporaryCachedEvents = cachedEvents
        let taksID = UIApplication.shared.beginBackgroundTask()
        let task = session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) -> Void in
            if(error != nil)
            {
                self.isSending = false
                UIApplication.shared.endBackgroundTask(taksID)
            }else {
                let httpResponse = response as! HTTPURLResponse
                if(httpResponse.statusCode >= 200 && httpResponse.statusCode <= 299) {
                    //success, Data was sent so we can create a new cached event
                    //remove all events we already sent
                    self.cachedEvents = self.cachedEvents.filter{!self.temporaryCachedEvents.contains($0)}
                    self.isSending = false
                    UIApplication.shared.endBackgroundTask(taksID)

                }else {
                    self.isSending = false
                    UIApplication.shared.endBackgroundTask(taksID)
                }
            }
        }
        task.resume()
    }
    func readCachedEventsFromDisk() {
        let dec = JSONDecoder()
        guard let data = UserDefaults.standard.data(forKey: BackendTrackingHandler.KEY_CACHE_EVENT) else {
            cachedEvents = []
            return
        }
        do {

            cachedEvents = try dec.decode([TrackingEvent].self, from: data)
        } catch {
            cachedEvents = []
        }
    }

    func writeCachedEventsToDisk() {
        let enc = JSONEncoder()
        let data = try! enc.encode(cachedEvents)
        UserDefaults.standard.set(data, forKey: BackendTrackingHandler.KEY_CACHE_EVENT)
    }

    override func onApplicationBecomeActive() {
    }

    override func onApplicationBecomeInactive() {
        let taskID = UIApplication.shared.beginBackgroundTask()
        writeCachedEventsToDisk()
        UIApplication.shared.endBackgroundTask(taskID)
    }
}

€ dit: TrackingEvent - это структура, которая используется несколькими TrackingHandler s. Существует еще FirebaseTrackingHandler, который предназначен для параллельной работы нашей собственной аналитической системы.

1 Ответ

1 голос
/ 11 марта 2020

Я думаю, что самый простой способ - написать "Property Wrapper" для cachedEvents, чтобы он напрямую обращался к UserDefaults. Кажется, операция не такая уж большая.

Второй способ - вы можете просто сохранить кэшируйте в UserDefaults каждые N секунд / минут или около того, если вы сильно заботитесь о производительности. Хотя это не сделало бы вашу систему пуленепробиваемой

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...