WCF 服務僅在客戶端收到結果時才寫入日誌 (WCF service writes log only if client receives results)


問題描述

WCF 服務僅在客戶端收到結果時才寫入日誌 (WCF service writes log only if client receives results)

I'm working on a WCF service to help our new code interoperate with a legacy system. The process goes like this:

  1. Client calls the service with a request for the legacy system.
  2. Service writes the request into a database.
  3. Legacy system services request from the DB in its own time and writes results back into the DB (updating a status flag to say results are ready).
  4. Client retrieves results by calling a second service method, which polls the DB until the ready flag is set.
  5. Just before returning the results, the service updates the status flag to client has results, so that the related DB rows can be deleted.

My concern is the race condition at the last step. I can see this happening:

  1. Service updates status to client has results.
  2. Client times out after waiting for the service to poll the DB.
  3. Service tries to return results. Hilarity ensues.

One way to solve this would be to have three service calls instead of two: the second call retrieves results, and the last one is an explicit acknowledgement by the client that it has them. I'd like to know whether there is a way which doesn't impose this extra "protocol" burden on the client though.

I've looked briefly into using transactions in WCF, and it sounds like they might be able to do what I need. The client (optionally) starts a transaction, flows it to the service, which uses it if it's there, and commits it when done. This seems as if it implicitly does the "third call".

Does this idea have any merit? Any disadvantages that you can see? Are there any other avenues I could explore?


參考解法

方法 1:

Why not reduce the likelihood of the client timing out by doing this instead: 

  1. Client calls service with a request for the legacy system.
  2. Service writes the request into a database.
  3. Legacy system services request from the DB in its own time and writes results back into the DB (updating a status flag to say results are ready).
  4. Client calls a service to find out whether the results are ready. NB. no polling: just returns with an immediate yes or no. 
  5. If the results are NOT ready, client waits a bit and then goes back to step 4. 
  6. If the results ARE ready, call the service to retrieve the results. The service can update the status to "Client has results" at that point. 

By doing this, the client won't be waiting for the service call in step 4. to return for a prolonged period, and the chances of a timeout should be minimal. 

However, you're never going to be 100% certain that the client has received the results unless the client makes a final service call to say so. (What if, for example, the client dies after making the very last request?)

方法 2:

Using transaction flow is possible but flowing transaction in polling scenario (in each poll call) is terrible architecture. What you generally need is transaction flow for the real read operation where service modifies the record and returns data back to the client. The client will commit the transaction and it will commit changes performed by the service.

Using transactional processing places some additional requirements on your service and clients.

Another approach can be transactional MSMQ:

  1. Client calls the service with a request for the legacy system = client sends a message to the service's queue
  2. Service writes the request into a database = service processes the message from its queue
  3. Legacy system services request from the DB in its own time and writes results back into the DB (updating a status flag to say results are ready).
  4. Service polls the database and places messages to correct client queues. Placing the message and modifying database records runs in transaction
  5. Client processes incoming message

Transactional queue allows transactional reading (the message is removed from the queue only if transaction is committed) and writing (the message is added to the queue only if transactions is committed). That will allow deleting records before the client reads the message because the message will remain in the queue until he successfully reads it (or until it timeouts and even after that it can be passed to some error queues).

In both cases you should think about clients who will consume the service. Transaction flowing can be interoperable but not every web service stack supports it. MSMQ is not interoperable.

(by anton.burgerrazlebeLadislav Mrnka)

參考文件

  1. WCF service writes log only if client receives results (CC BY-SA 3.0/4.0)

#race-condition #transactions #distributed-transactions #wcf






相關問題

Javascript 和 DOM 事件交互和可能的競爭條件 (Javascript and DOM event interaction and possible race conditions)

防禦 System.Collections.Concurrent.ConcurrentDictionary 中的競爭條件 (Defending against race conditions in System.Collections.Concurrent.ConcurrentDictionary)

可能一次在 PHP 中多次寫入同一個文件? (Potentially write to same file in PHP multiple times at once?)

靜態線程安全 (Thread safety of static)

CancellationTokenSource.Cancel 引發 ObjectDisposedException (CancellationTokenSource.Cancel throws an ObjectDisposedException)

如何處理 Web 應用邏輯和數據庫並發? (How to handle Web application logic and database concurrency?)

Linux IRQ 處理程序中的固有競爭條件 (Inherent race condition in Linux IRQ handlers)

SQL Server 進程隊列競爭條件 (SQL Server Process Queue Race Condition)

WCF 服務僅在客戶端收到結果時才寫入日誌 (WCF service writes log only if client receives results)

將 SWT 與 JOGL 一起使用時發生隨機崩潰(競爭條件?) (Random crashes when using SWT with JOGL (race condition?))

std::shared_ptr 的線程安全 (Thread safety with std::shared_ptr)

如何更新 JSON 類型列中的特定值 (How can I update specific value from my JSON type column)







留言討論