当我创建新客户时,我的应用程序有一些沉重的回调验证。基本上我在创建新客户记录之前检查多个API以查看是否存在匹配。我不希望在创建之后发生这种情况,因为如果没有任何匹配,我宁愿不首先保存记录。
我有一个创建新客户的webhook设置。问题是,因为我的客户验证花了这么长时间,所以webhook继续开火,因为它没有得到立即响应。
这是我的客户模型:
validates :shopify_id, uniqueness: true, if: 'shopify_id.present?'
before_validation :get_external_data, :on => :create
def get_external_data
## heavy API calls that I don't want to perform multiple times
end
我的钩子:
customer = shop.customers.new(:first_name => first_name, :last_name => last_name, :email => email, :shopify_url => shopify_url, :shopify_id => id)
customer.save
head :ok
customer.save大约需要20秒。
澄清一下,问题在于:
Webhook被解雇
进行重型API调用
第二个Webhook被触发(API调用仍然来自第一个webhook)。运行重API调用
第三个Webhook被解雇
直到最后保存第一条记录才会发生这种情况,以便我现在可以检查以确保shopify_id是唯一的
有解决方法吗?我如何进行防御性编程以确保没有重复记录开始处理?
答案 0 :(得分:2)
多么有趣的问题,谢谢。
<强>异步性强>
这里的主要问题是依赖于外部网络钩子。
测试这些所需的延迟不仅会影响您的保存时间,还会阻止您的服务器处理其他请求(除非您使用某种多处理)。
让您的流程依赖于多个外部资源通常不是一个好主意。在这种情况下,它是合法的。
我唯一真正的建议就是让它成为异步流程......
-
Asynchronous vs synchronous execution, what does it really mean?
当您同步执行某些操作时,请等待它完成 然后继续进行另一项任务。当你执行某些事情 异步地,您可以在完成之前继续执行另一项任务。
在JS中,最着名的异步制作示例是使用Ajax
回调... IE通过Ajax发送请求,使用某种“等待”过程来保持用户更新,然后返回响应。
我建议为前端实现此功能。后端必须确保在处理外部API调用时服务器的手不受限制。这可能需要使用系统的其他部分(不需要使用Web服务器进程),或将功能分成其他格式。
<强>的Ajax 强>
我绝对会在前端使用Ajax,或另一种异步技术(web sockets?)。
无论哪种方式,当用户创建帐户时,我都会创建一个“待处理”屏幕。使用ajax是最简单的例子;但是,它在范围上受到很大限制(IE如果用户刷新页面,他就失去了联系)。
也许有人可以提出一种在异步系统中恢复状态的方法吗?
您可以使用Ajax callbacks处理它:
#app/views/users/new.html.erb
<%= form_for @user, remote: true do |f| %>
<%= f.text_field ... %>
<%= f.submit %>
<% end %>
#app/assets/javascripts/application.js
$(document).on("ajax:beforeSend", "#new_user", function(xhr, settings){
//start "pending" screen
}).on("ajax:send", "#new_user", function(xhr){
// keep user updated somehow
}).on("ajax:success", "#new_user", function(event, data, status, xhr){
// Remove "pending" screen, show response
});
这将为您提供一个不会堵塞服务器的前端流程。在请求处理过程中,您仍然可以在页面上执行“填充”操作。
-
<强>队列强>
第二部分将与您的服务器处理请求的方式有关。
具体来说,它是如何处理API请求的,因为它们会导致延迟。
目前我能想到的唯一方法是排队请求,并有一个单独的过程通过它们。这里的主要好处是它会使你的Rails应用程序的请求异步,而不必等待响应的到来。
您可以使用Resque之类的gem对请求进行排队(它使用Redis),允许您将请求发送到Resque队列&amp;捕捉它的反应。然后,此响应将形成您对ajax请求的响应。
在执行此操作之前,您可能需要设置一个临时用户:
#app/models/user.rb
class User < ActiveRecord::Base
after_create :check_shopify_id
private
def check_shopify_id
#send to resque/redis
end
end
当然,这是一个非常高级别的建议。希望它能为您提供更好的视角。
答案 1 :(得分:1)
这是一个棘手的问题,因为您的客户创建取决于昂贵的验证。我看到了一些可以减轻这种情况的方法,但它将是一种“较小的邪恶”类型决定:
create
。这需要一个cron作业来保持客户列表的更新。validated
标志,然后在后台任务中运行一次检查。如果客户存在,则与现有客户合并;如果没有,请将客户标记为有效。任何一种选择都需要解决,以避免昂贵的电话。