奇妙な動作をしています。ローカル マシンでは複製できず、気が狂い始めています。
ASP.NET MVC がアクションを実行しようとしているように見えますが、何かがタイムアウトし、例外なく失敗し、ajax クライアントに通知され、アクションを再実行しようとすると、ajax クライアントは応答を取得しますが、元の呼び出しからではありません。
私はコントローラーアクションを持っています:
[ValidateAntiForgeryToken]
[ClientErrorHandler]
public virtual ActionResult PlaceOrder(CheckoutDto checkoutDto)
{
LoadDataFromDatabase();
// this may take up to couple minutes
var orderConfirmationData = PlaceOrderToExternalWebservice();
SaveConfirmationData(orderConfirmationData);
return View(Transform(orderConfirmationData))
}
そして、jquery ajax を使用して呼び出します。
$.ajax({
url: placeOrderActionUrl,
type: "POST",
async: true,
dataType: "html",
data: $('#checkoutForm').serialize(),
success: function (data) {
// show confirmation data
},
error: function (request, status, error) {
// show error message
}
});
小規模な注文の場合は正常に機能しますが、大規模な注文の場合は 2 つの注文が作成され、その理由は処理時間のようです。注文が大きいほど、外部 Web サービスへの配置に時間がかかります。
IIS ログをチェックして、クライアント スクリプトがアクションを 2 回呼び出していないことを確認しました。IIS ログには、特定のアクションへの呼び出しが 1 回しか表示されません。
外部サービスは失敗しません。イベント ログ/SQL ログに記録される例外はありません。
確認するために、その ajax クライアントは、元の呼び出しからではなく、ロックのようなものを作成しました。
[ValidateAntiForgeryToken]
[ClientErrorHandler]
public virtual ActionResult PlaceOrder(CheckoutDto checkoutDto)
{
try
{
if (OrderingLockedForCurrentUser())
{
Log("Locked");
return View("Already placing order");
}
LockOrderingForCurrentUser();
LoadDataFromDatabase();
// this may take up to couple minutes
var orderConfirmationData = PlaceOrderToExternalWebservice();
SaveConfirmationData(orderConfirmationData);
return View(Transform(orderConfirmationData))
}
finally
{
RemoveOrderingLockForCurrentUser();
}
}
そして、確定データを返す代わりに、「発注済み」を返します。
おそらくアクション実行のタイムアウトだと思っていましたが、念のため試してみました
<httpRuntime executionTimeout="600" />
助けにはなりませんでした。
追加のロギングを有効にするために、理由を検索する場所、追加で確認するものはありますか?
更新: 興味深いことに、元の呼び出しも完了しています。
更新 2: アクション フィルター AjaxOnly を追加して、JavaScript からのみ呼び出されるようにしました。
public class AjaxOnlyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
throw new Exception("This action is intended to be called from Ajax only.");
}
}
}
そしてログからはjavascriptからしか呼び出されないので謎が続きます…
更新 3:
別のテストコントローラーで単純なスレッドスリープに問題を分離しました:
[ValidateAntiForgeryToken]
[AjaxOnly]
[ClientErrorHandler]
public virtual ActionResult PlaceOrderAction(CheckoutDto checkoutDto)
{
try
{
if (CanPlaceOrder(Request.RequestContext.HttpContext))
{
Thread.Sleep(TimeSpan.FromSeconds(90));
return Content("First time");
}
return Content("Second time");
}
finally
{
HttpContext.Cache.Remove(GetKey(userService.CurrentUser.UserId));
}
}
public bool CanPlaceOrder(HttpContextBase httpContext)
{
var userId = userService.CurrentUser.UserId;
var key = GetKey(userId);
if (httpContext.Cache[key] == null)
{
httpContext.Cache.Add(key, userId, null, DateTime.Now.AddMinutes(10), new TimeSpan(), CacheItemPriority.High, null);
return true;
}
return false;
}
private static string GetKey(int userId)
{
return "PlacingOrder{0}".With(userId);
}
2 つの独立した開発マシン (win 7) と ec2 のステージング マシン (win2008sp2) で問題なく動作する限り、運用サーバーの IIS 設定 (win 2008R2 x64 sp1) に問題があることはほぼ確実です。