7

どこから始めればよいか、どの情報が関連しているかわかりません。この問題の解決に役立つ可能性のある追加情報を教えてください。

私は単純な cometd アプリケーションを開発しており、ストレージ バックエンドとして mongodb を使用しています。アプリケーションの起動時に単一の mongodb インスタンスを取得し、このインスタンスをすべてのクエリに使用します。これは、実際には、http://www.mongodb.org/display/DOCS/Java+Driver+Concurrency に記載されているように、mongo Java ドライバーのドキュメントで推奨されています。問題はスレッドセーフに関係していると考えてストローをつかんでいましたが、そのリンクによると、mongodbは完全にスレッドセーフです。

ここが興味深いところです。を拡張するクラスがありますBasicDBObject

public class MyBasicDBObject {

    private static final String MAP = "map";

    public boolean updateMapAnd(String submap, String key, byte[] value) {
         Map topMap = (Map)this.get(MAP);
         Map embeddedMap = topMap.get(submap);
         byte[] oldValue = embeddedMap.get(key);

         newValue = UtilityClass.binaryAnd(oldValue, value);

         embeddedMap.put(key, newValue);
         topMap.put(submap, embeddedMap);
         this.put(MAP, topMap);
    }

    public boolean updateMapXor(String submap, String key, byte[] value) {
         Map topMap = (Map)this.get(MAP);
         Map embeddedMap = topMap.get(submap);
         byte[] oldValue = embeddedMap.get(key);

         newValue = UtilityClass.binaryXor(oldValue, value);

         embeddedMap.put(key, newValue);
         topMap.put(submap, embeddedMap);
         this.put(MAP, topMap);
    }
}

を拡張する次の 2 つのスケルトン クラスMyBasicDBObject

public class FirstDBObject extends MyBasicDBObject { //no code }

public class SecondDBObject extends MyBasicDBObject { //no code }

このようにクラスを設定した唯一の理由は、同じスコープ内でこれら 2 つのオブジェクトを処理する際にコードを読みやすくするためです。これにより、次のことができます...

//a cometd service callback
public void updateMapObjectsFoo(ServerSession remote, Message message) {

    //locate the objects to update...
    FirstDBObject first = (FirstDBObject) firstCollection.findOne({ ... });
    SecondDBObject second = (SecondDBObject) secondCollection.findOne({ ... });

    //update them as follows
    first.updateMapAnd("default", "someKey1", newBinaryData1);
    second.updateMapAnd("default", "someKey2", newBinaryData2);

    //save (update) them to their respective collections
    firstCollection.save(first);
    secondCollection.save(second);
}

public void updateMapObjectsBar(ServerSession remote, Message message) {

    //locate the objects to update...
    FirstDBObject first = (FirstDBObject) firstCollection.findOne({ ... });
    SecondDBObject second = (SecondDBObject) secondCollection.findOne({ ... });

    /** 
     * the only difference is these two calls 
     */
    first.updateMapXor("default", "someKey1", newBinaryData1);
    second.updateMapXor("default", "someKey2", newBinaryData2);

    //save (update) them to their respective collections
    firstCollection.save(first);
    secondCollection.save(second);
}

渡されたバイト配列を反復処理することによりUtilityClass、メソッドの名前、ビット単位、およびビット単位とまったく同じように動作します&^

これは私が完全に迷っているところです。 updateMapObjectsFoo()期待どおりに機能し、データベースの変更firstsecond反映します。 updateMapObjectsBar()一方、適切に更新することしかできませんfirst

デバッグによる検査updateMapObjectsBar()では、バイナリ オブジェクトが実際には両方のオブジェクトで適切に更新されていることが示されていますが、問題を調査するために mongo シェルに向かうとfirst、DB で更新されていて更新されてsecondいないことがわかります。スレッドセーフがそれと関係があるという考えはどこから得たのでしょうか? 私を悩ませている唯一の違いは、それsecondCollectionが他の cometd サービスで使用されているのに使用されてfirstCollectionいないことです。それは一方では関連しているように見えますが、機能し、機能しないため、他方ではそうではFooありBarません。

コードを分解して元に戻しましたが、この同じ問題に何度も戻ってきます。ここで何が起こっているのですか?

Javaジェネリックの悪夢と、言語のこの機能へのmongodbドライバーの依存である、すべての中で最も関連性のある部分を省略したようです。 BasicDBObjectは本質的に のラッパーですMap<String, Object>。問題は、オブジェクトをそのマップに格納すると、そこに配置したときの状態に戻す必要があることです。はい、それは完全に明白に思えるかもしれません。この質問を投稿する前に、私はそれを十分に知っていました。

何が起こったのかを正確に特定することはできませんが、Java + mongodb ユーザーにこのアドバイスを提供します。多くのキャストを行い、データ構造が複雑になればなるほど、より多くのキャストが必要になります。簡単に言うと、次のことはしないでください。

DBObject obj = (DBObject) collection.findOne(new BasicDBObject("_id", new ObjectId((String)anotherObj.get("objId"))));

ラピッド プロトタイプを作成しているときは、ワンライナーは魅力的ですが、それを何度もやり始めると間違いを犯すことになります。今より多くのコードを書いて、後でフラストレーションを感じないようにしましょう:

DBObject query = new DBObject();
String objId = (String) anotherObj.get("objId");
query.put("_id", new ObjectId(objId));
obj = (DBObject) collection.findOne(query);

これは煩わしいほど冗長だと思いますが、ある種のライブラリを使用する代わりに、Mongo と直接対話することで、生活が楽になることを期待する必要があります。私はこれで自分をばかにしてしまいましたが、誰かが私の過ちから学び、多くのフラストレーションを救ってくれることを願っています.

ご協力いただきありがとうございます。

4

1 に答える 1

2

非常に簡単にマルチスレッドの問題になる可能性があります。Mongo インスタンスが 1 つしかない場合、Mongo、DB、および DBCollection オブジェクトはスレッドセーフであることは間違いありませんが、DBObjects はスレッドセーフではありません。しかし、それらがスレッド セーフであったとしても、updateMapObjectsFoo/Bar メソッドは、それらがデータベースでのアトミック操作であることを保証するために何もしません。

残念ながら、コードに加える必要がある変更は、いくつかの「同期化された」キーワードを散りばめるだけではありません。http://www.mongodb.org/display/DOCS/Atomic+Operationsが問題の範囲と考えられる解決策を理解するのに役立たないかどうかを確認してください。

于 2012-10-22T18:21:21.910 に答える