iOSアプリの行動ログをTreasure Dataへ登録しようとしたらデータが重複してはまった話


TDにiOS向けのSDK(td-ios-sdk)で、起動時のログを登録しようとしたら、サーバサイドに登録したデータが重複してたよという話題です。(多分仕様です。)

やりたかったことの概要

アプリの起動時にいろいろな箇所でaddEvent()をやって、サーバにuploadしたかどうかは管理したくないので、addEvent()した直後にuploadEvents()を都度実行するといったことをやってみました。

コードイメージ

TreasureData.sharedInstance().addEvent(["msg":"first"], database: "test_app", table: "test_table")
TreasureData.sharedInstance().uploadEvents()

TreasureData.sharedInstance().addEvent(["msg":"second"], database: "test_app", table: "test_table")
TreasureData.sharedInstance().uploadEvents()

結果

firstとsecondのレコードがそれぞれ2件作られました。

重複に関する調査

その1

TDサイトに最後に登録された10分間にUUIDが重複していたら削除すると書かれています。

その2

サーバー側でイベントの重複排除を行っているためアップロードが複数回行われてもデータの重複を防止(現在、1日間の重複排除を実施)

td-ios-sdk のコミッタの方の記事でサーバーサイドで重複に対する考慮されているとありました。

SDKでレコードを追加すると、レコード毎に値が異なるuuidカラムが暗黙のうちに追加されていて、enableAutoAppendRecordUUID()との違いがよくわっていなかったのですが、uuidカラムを使って重複削除する仕組みのようです。ただ、実験した環境だと動いていないようでした。

回避をこころみた

やってみたこと

uploadEventsメソッドにはコールバックを受け付けるバリエーションがあります。これを使って、uploadEventsを直列化すれば重複を回避できそうです。

TreasureData.sharedInstance().uploadEventsWithCallback(
    {
        //成功した場合
    },
    onError: {errCode,errMsg in
        //失敗した場合
    }
)

//dispatch_sync()とかdispatch_semaphore_waitとかで直列に処理する

結果

Request data is empty とログがでて、直列化の途中で詰まりました。内部のKeenClientクラスで、アップロードするデータが無い場合は、成功のブロックも、失敗のブロックも呼ばない仕様のようです。

今回のまとめ

さがせば書いてあるのかもしれませんが、公式のドキュメントとgithubのプロジェクトを1日くらい眺めていましたが、特にこれ以上の情報は見つけられませんでした。サーバに登録されたレコードには、timeカラムが自動的に付与されて処理時間が記録されるのですが、ミリ秒以下の時間は丸められています。ディフォルトだと秒単位でレコードを記録をする仕組みなので、1秒間に何度もイベントを記録するといった用途はそもそも向いていないのかもしれません。