2011-07-15 7 views
2

CUSTOMERドキュメントを更新して同じトランザクションで電子メールを送信する必要がある場合、これが原子的に確実に行われるようにするにはどうすればよいでしょうか?私たちはeコマースのウェブサイトを構築しています。顧客が商品を購入したときに注文履歴を更新して確認メールを送信する必要があるため、この機能が必要です。 RDBMSデータベースを使用するJavaでは、データベースを簡単に更新し、電子メールの内容と詳細を含むJMSメッセージを送信することで、これを簡単に実行できます。 JDBCとJMSの両方が分散トランザクションをサポートしているので、何かがうまくいかないとロールバックできますが、MongoDBではそうではありません。 Mongoにメッセージング機能はありますか?MongoDBマルチステップトランザクションの問題

私たちは、お客様のorderHistory埋め込まれた文書にフラグ「emailSentFlag」を使用する方法について考えていました。注文が行われると、フラグはfalseに設定されます。 emailSentFlag = "false"のすべての注文履歴をスキャンしてその時点でメールを送信する外部ジョブを使用しますが、送信後にフラグを "true"に戻す必要があるため、同じ状況に戻ります電子メール、それはアトミックではありません。

> customer { 
>  name: 
>  email: 
>  orderHistory{ 
>  orderId: 
>  status: 
>  emailSentFlag: 
+0

注意してください! MongoDBはトランザクションをサポートしていません! http://www.mongodb.org/display/DOCS/Developer+FAQ#DeveloperFAQ-HowdoIdotransactions%2Flocking%3F – DavidJBerman

+0

私はそれを知っていますが、私は両方の操作を一緒に働かせる必要があります。 – raffian

+0

JMSは、メールが1回だけ正確に送信されることをどのようにして確認しますか?確かに、それをキューに入れて少なくとも1回は処理されるようにすることができますが、smtpにメールを送信した直後に、完了したジョブを設定する前にクラッシュする可能性があります。何か不足していますか? –

答えて

1

あなたは、ドキュメントレベルでアトミック操作を行うことができます。私はあなたの電子メールをキューイングのためのコレクションを持っていると思います http://www.mongodb.org/display/DOCS/Atomic+Operations

、および電子メールがあるときにレコードを更新するために、電子メール、文書内のフィールドを置きます送信されます。次に、送信が完了したら、送信ジョブを親レコードに更新させます。

あなたも、彼の顧客の文書TOT電子メール文書からDBのRefを使用することができます。

+0

私たちはちょっと同じラインに沿って考えていましたが、あなたの提案に同じ問題がありますか?親レコードを更新して電子メールを送信することは、Mongoでは不可分ではありません。 – raffian

+1

私のコメントをチェックしてください。私は本質的にトランザクションではない何か(電子メールへの電子メール)の上にトランザクションを構築できるとは思わない。確かに、それは一年に一度のようにクラッシュし、あなたは再びそのメールを再送します。誰も実際に複製メールについて不平を言うことはありません。 –

1

これは、これらのアクションは本当にアトミックに発生する必要があるかどうかを考える価値がある:

  • 注文履歴ラインアイテムに追加
  • 送信確認メール

電子メールを送信するためのロールバックの基準になりますどのような?無効なメールアドレス?メールサーバーに接続できませんでしたか?メールは送信されましたが受信されません メールサーバーが停止しているため、顧客の取引をキャンセルしますか?

customer.orderHistory.emailSentFlagで新しい注文ラインを作成してから、emailSentFlag = 0注文を処理して確認メールを送信し、その後メールが正常に送信されたらフラグを設定するようにスケジュールされた仕事をしてもいいと思います。

バウンスバックが発生した場合、または何らかの理由で配信が失敗した場合は、いつでもフラグをリセットして再試行できます。それとも、メールの仕事をメールキューイングシステムに送り込んで、あなたのためにすべてのものを処理してください。

2

モンゴーでの取引は、既に他人から言われているようにはありません。

私はあなたがフラグを設定するのではなく、メールキューを構築し、コレクション全体を検索することをお勧めします。私はむしろメールキューにエントリを入れて、外部ジョブを使ってそのキューを処理します。

少なくともこれを最初にやろうとします。誰でも、コメントは良いか悪いですか?

0

これはスキーマの少ないデータベースなので、活用してください! :-)ダックタイピングを使用できますが、タイプ間で認識される任意のドキュメントタイプにフィールドを追加することができます。ドキュメントで使用されていない名前を必ず使用してください。

メールを送信するなどのタスクがあるドキュメントでは、tasks: []の配列を追加します。ドキュメントを更新し、それに関連付けられた「アトミック」なものを持つ必要がある場合、このキューにタスクをプッシュします。たとえば、メールの場合:{ email: "[email protected]", at: 0 }。バックグラウンドプロセスは、これらのタスクをポーリングし、将来それらが期限切れになるように設定し、実行し、それらを削除します。どこかで何かが失敗した場合、タスクは期限切れになり、再実行されます。だから、あなたは2通のメールを送るかもしれませんが、それはトランザクションでさえメールでは不可避です。

あなたはMongoDBのシェルでこれを実行できます。

#db.orders.remove() 
db.orders.save({ name: "abc", tasks : [ { email: '[email protected]', at: 0}, { email: '[email protected]', at: 0} ] }) 
db.orders.save({ name: "def", tasks : [ { email: '[email protected]', at: 0}] }) 

now = 10; future = 100; me = "some unique identifier for this scanning process" 

while (true) { 
    task = db.orders.findAndModify({ 
    query: { "tasks.at": { $lt: now }}, 
    update : { $set : { "tasks.$.at": future, "tasks.$.who": me } }, 
    fields: { "tasks":1}, 
    new:  true }) 

    if (!task) 
    break; 

    for (var i in task.tasks) { 
    if (task.tasks[i].who === me) 
     print("Send email " + task.tasks[i].email) 
    } 

    db.orders.update({ _id: task._id }, { $pull: { tasks: { who: me } } }) 
} 

は同様の例のためにhttp://www.mongodb.org/display/DOCS/findAndModify+Commandを見てください。