首页 > 解决方案 > 如何正确防止分析数据的数据丢失?

问题描述

我目前正在编写一个分析系统。目前,它在 RAM 中缓存事件。当应用程序关闭时,它通过 NSUserDefaults (iOS) 和 SharedPreferences (Android) 以 JSON 格式写入文件系统。当应用程序打开时读取此数据。

它还会每 N 秒发送一次或在事件数量达到 20 时发送。发送成功时,它会删除从 RAM 发送的所有事件。

这有一些明显的缺陷:当应用程序崩溃时,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是一个在多个TrackingHandlers 之间共享的结构。还有一个额外的FirebaseTrackingHandler,旨在与我们自己的分析系统并排运行。

标签: iosswiftoptimization

解决方案


我认为最简单的方法是编写“Property Wrapper”,cachedEvents这样它就可以直接访问 UserDefaults,看起来这个操作并不那么麻烦。

第二种方式 - 如果您非常关心性能,您可以每隔 N 秒/分钟左右将缓存保存到 UserDefaults。但是,它不会使您的系统防弹


推荐阅读