2009-02-24 26 views
9

我必須發送電子郵件,寫入文件並調用Web服務。爲了保持一致性,所有步驟都必須發生。如果任何步驟拋出異常或錯誤,則必須回退所有步驟。用於在數據庫之外實現事務的模式

我走我自己的滾動對象ACID引擎之前,是否有對象級別實現ACID語義的普遍接受的模式?

更好的是,是否有任何現有的庫可用於.NET平臺?

編輯︰我知道發送電子郵件不能撤消,但未能連接到SMTP服務器是導致殺死整個交易。此外,我希望這可以擴展用於未來的行動。

+0

如何回滾電子郵件? – mbeckish 2009-02-24 03:23:44

+0

也許發送召回:) – 2009-02-24 03:24:54

回答

5

我最後一次看到這樣的事情是幾年前。我記住的一點是,它使用命令模式並將每個命令對象存儲在一個隊列中。我認爲這是一個LIFO堆棧。

因此,如果「交易」失敗時,發動機就會關閉彈出一個命令對象,撤消命令,然後銷燬命令對象。重複,直到堆棧爲空。如果「交易」成功,則堆棧被清除。

不幸的是,我不記得比這更多。

CSLA.NET實現了一個類似的撤消堆棧。這是代碼的唯一例子,我可以從頭腦中思考。

3

當ACID語義可能不合適時,Windows Workflow Foundation的概念爲compensation(使用複合活動)。當然,它也支持ACID事務。

一個好問題是爲什麼要用 賠償?是不是一個大的ACID 交易與自動回滾 一樣好?當在相同的數據庫中或在相同的信息系統內發生操作 時,ACID事務是最合適的 。當操作快速結束 時,它也是最合適的 。當涉及不同的公司和服務時,根據ACID語義 定義 過程通常是具有挑戰性的。因爲它是孤立和耐用的 ,您必須保留 不同公司 的所有資源在任務期間鎖定。 這通常是不合理的,特別是如果任務很長的話。對於它 是一致的和原子的,您需要 臨時補償代碼。

1

由於無法取消發送一封電子郵件,這是相對便宜寫一個文件,我只是做這些事情按正確的順序:

  1. 嘗試寫入文件/寫入文件。如果不成功,請停止,否則繼續:
  2. 調用Web服務。如果不成功,請刪除文件並停止,否則繼續:
  3. 發送電子郵件 - 電子郵件是異步的,所以你永遠不會知道它是否被髮送,因爲大多數電子郵件服務器都設置爲重試幾天後如果發生錯誤,並且您永遠不會收到確認電子郵件已經通過,即使它已成功
3

不依賴外部庫的最簡單的技術是prevalence。定期使用檢查點通過使用序列化來拍攝您的狀態快照,然後通過序列化足夠的信息對您的數據進行每次有效操作以便稍後重複,從而維護日誌。如果有事情發生,請重新加載最近的檢查點,然後重新應用該點後寫入的所有日誌記錄。

對於更復雜的東西,請嘗試software transactional memory。在當前語言中實現可能有點笨拙,但功能非常強大,並且可能會給你一些額外的併發技術。

對於不可逆轉的操作,如訪問Web服務或發送電子郵件,您需要使用compensating transactions:進行另一個Web服務調用以取消或更新前一個Web服務的結果,或者發送另一封電子郵件,通知收件人事情沒有按預期工作。

1

一個思路歸結爲簡單的(但仍然很有用)的建議是使用JMS的「引擎」,你可以使用JMS交易(可加入現有的交易如數據庫事務)。這開始導致異步事件驅動的架構,這可能是一件好事,除非我們正在談論簡單的應用程序 - 在這種情況下,你可能不需要問這個問題。

一個這樣的例子就是簡單的賬戶創建。爲此,您需要將帳戶信息持久保存到數據庫,並向用戶發送激活郵件 - 但您希望他們在同一事務中出於顯而易見的原因。

您不應該在交易中放置電子郵件發送代碼,因爲即使您可能發送電子郵件--db事務提交可能因某種原因失敗。 您也不應該在交易之外(提交後)發送電子郵件,因爲電子郵件發送可能會導致孤兒帳戶失敗。

因此,在這種情況下使用JMS--將JMS發送代碼發送到數據庫事務中並讓它加入該事務。你保證信息傳遞。另一方面有消耗隊列發送電子郵件的東西。在電子郵件發送失敗的情況下,最好的選擇是記錄/提出警報 - JMS將回滾並將消息放回隊列供以後使用。即一旦您希望解決問題,就嘗試重新發送電子郵件。

關鍵的是 - 數據庫記錄是一致的,最終發送郵件。

0

此外,最近發佈了一個實驗項目STM .NET。這個項目將事務內存添加到C#中。它實際上修改了CLR來支持這一點。

相關問題