2012-11-27 3 views
28

私はアプリケーションエンジンでJDO 2.3を使用しています。私はローカルテストのためにマスター/スレーブデータストアを使用していましたが、最近ローカルテストにHRDデータストアを使用するように切り替えました。私のアプリケーションの一部は壊れています(これは予想されます)。アプリが壊れている部分の1つは、たくさんの書き込みをすばやく送信する場所です。これは1秒の制限があるため、同時の変更の例外で失敗しています。コミットされたJDOの書き込みは、ローカルGAE HRD、または場合によっては再利用されたトランザクションには適用されません。

これも期待していますので、後で書き込みが失敗したときにブラウザが再試行してしまいます(おそらく、最高のハックではないかもしれませんが、すぐに動くようにしようとしています)。

しかし、奇妙なことが起こっています。コミットフェーズが完了し、要求が私の成功コードを返すにもかかわらず、成功するはずの書き込み(同時の変更例外を受け取らないもの)も失敗します。再試行されたリクエストは正常に動作していることがログからわかりますが、最初の試行でコミットしたと思われるこれらのリクエストは、決して「適用されません」。しかし、私が「適用」フェーズについて読んだことから、同じエンティティに再度書き込むと、適用を強制するはずですが、そうではありません。

コードが続きます。注意すべき事項:

  1. automatic JDO cachingを使用しようとしています。そこで、JDOはmemcacheを使っています。これは、トランザクション内のすべてをラップしない限り、実際には機能しません。
  2. エンティティから文字列を読み込み、文字列の一部を変更し、その文字列をエンティティに保存し直すことです。これらのリクエストがトランザクションに含まれていない場合は、もちろん「ダーティリード」の問題があります。しかし、トランザクションでは、分離は "シリアライズ可能"のレベルにあるはずですので、私はここで何が起こっているのか分かりません。

    PersistenceManager pm = PMF.getManager(); 
    Transaction tx = pm.currentTransaction(); 
    String responsetext = ""; 
    try { 
        tx.begin(); 
        // I have extra calls to "makePersistent" because I found that relying 
        // on pm.close didn't always write the objects to cache, maybe that 
        // was only a DataNucleus 1.x issue though 
        Key userkey = obtainUserKeyFromCookie(); 
        User u = pm.getObjectById(User.class, userkey); 
        pm.makePersistent(u); // to make sure it gets cached for next time 
        Key mapkey = obtainMapKeyFromQueryString(); 
        // this is NOT a java.util.Map, just FYI 
        Map currentmap = pm.getObjectById(Map.class, mapkey); 
        Text mapData = currentmap.getMapData(); // mapData is JSON stored in the entity 
        Text newMapData = parseModifyAndReturn(mapData); // transform the map 
        currentmap.setMapData(newMapData); // mutate the Map object 
        pm.makePersistent(currentmap); // make sure to persist so there is a cache hit 
        tx.commit(); 
        responsetext = "OK"; 
    } catch (JDOCanRetryException jdoe) { 
        // log jdoe 
        responsetext = "RETRY"; 
    } catch (Exception e) { 
        // log e 
        responsetext = "ERROR"; 
    } finally { 
        if (tx.isActive()) { 
         tx.rollback(); 
        } 
        pm.close(); 
    } 
    resp.getWriter().println(responsetext); 
    
  3. エンティティが変更されて、私は、クロスグループトランザクションは

関連するコード(これは単純化されたバージョンである)を有効にしている(いないグループで)ルートエンティティ

  • あります

    更新:なぜこのようなことが起こっているのか、私は確信していますが、それでも確認できる人に賞金を授与します。

    基本的に、トランザクションは実際にはデータストアのローカルバージョンで実装されていないという問題があると思います。参考文献:トランザクションが実装されていませんので

    https://groups.google.com/forum/?fromgroups=#!topic/google-appengine-java/gVMS1dFSpcU https://groups.google.com/forum/?fromgroups=#!topic/google-appengine-java/deGasFdIO-M https://groups.google.com/forum/?hl=en&fromgroups=#!msg/google-appengine-java/4YuNb6TVD6I/gSttMmHYwo0J

    、ロールバックは基本的に何もしません。したがって、2つのトランザクションが同時にレコードを変更しようとすると、不正な読み取りが行われます。つまり、Aはデータを読み取り、Bは同時にデータを読み取ります。 Aはデータの変更を試み、Bはデータの異なる部分の変更を試みます。 Aはデータストアに書き込み、次にBはAの変更を消去します。その後、Bはアプリエンジンによって「ロールバック」されますが、ローカルデータストアで実行中のロールバックは実行されないため、Bの変更はそのまま残り、Aは実行しません。一方、Bは例外をスローしたスレッドであるため、クライアントはBを再試行しますが、Aは再試行しません(Aは成功したトランザクションだったため)。

  • +0

    ネイティブな方法を学ぶ価値がある、あなたはより多くの1秒に1回以上、同じエンティティグループに永続化を避けるために、データストアとどのようにあなたがそれを使用するの再設計を考えたことがありますか?また、タスクをエンキューするためにデータストアに永続化を渡してみましたか、1/sエンティティグループ書き込み頻度制限を尊重するように設定しましたか? –

    +0

    私はそれを考えました。しかし、私がそれをする前に、なぜこの特定のバグが起こっているのか理解したいと思います...私の懸念は、HRDやアプリエンジン/ jdoトランザクションや何かについて基本的に何かを理解していないか、ドキュメント、そして私は、現在のプラグイン(GAEを使用して、私はにトランザクションを追加する必要があり、少なくとも25個の他のサービス(データストアアクセスはトランザクション内にない場合JDOのキャッシュは動作しません) – eeeeaaii

    +0

    FWIWを、持っているので、後で私を噛まないように起こっていますJDO v2.x)、私はL2キャッシュが動作するためのトランザクションにアクセスする必要はないと考えています。もしオブジェクトが読み込まれてL2キャッシュされていれば、それが報告されなければなりません(明らかに古いプラグインはサポートされていません。 – DataNucleus

    答えて

    1

    おそらく悪いニュース、私はJDOを去り、Objectifyを使用していて、いくつかの場所で直接データ核を使用しています。私は、あなたが長期的に考えるならば、パフォーマンスとデザインのよりよい選択である私の永続性を完全に制御します。

    デシベルは何-SQLではありませんので、JPA、JDOと標準仮定に対する構造的な変化があります。

    はあなたが標準JPAですら客観化していないことを行うことができますネイティブDataNucleusのAPIを使用:例では、私は動的に

    トランザクションは時々トランザクション(エンティティグループ)のように見えることができる何かがある、GAEに存在しない列を作成するために使用しました。したがって、ネイティブAPIを使用すると、このような不可解な体操をすることは避けられます。ジョイスティックで車を運転しようとすると

    は仕事ができるが、学ぶべき新しいものは確かにあります。私の意見では、

    +1

    Ziedが正しいです。 GoogleがJPAとJDOの抽象レイヤーを提供するのはやや面白いですが、私はそれほどうまくいきませんでした。最も一般的には、RDBMSとしてバックエンドデータストアを考え続けるという事実のため、通常、あらゆる種類の不快なハックで終わります。私は、JPAまたはJDOライブラリを使用せず、生のGoogle APIまたはObjectifyを使用することを強く勧めます。 –

    関連する問題