您不想同时运行的方法

控制器
def foo
	AggregateTablesService.new.call # 数十万行のレコードの集計を行い、別のテーブルに集計を保存するサービス
	@something = CreateGraph.new.call # 集計結果からグラフを生み出しキャッシュに保存するサービス。キャッシュがあればそれを読み込む
end
批处理(每天午夜轮换)
def exec
	AggregateTablesService.new.call
	CreateGraph.new.call
end

在处理大量复杂记录时,我们假设有以下过程:1)将它们汇总到汇总表中,以便于处理和加快处理速度;2)使用汇总数据创建图表。

首先,可以通过说“如果有记录或缓存,则以return退出进程”来处理多次运行的方法。

聚合服务
class AggregateTablesService
	def call
		return if SummaryTable.where().present? # もしすでに集計されていたら処理を抜ける

		# ~~~具体的な処理〜〜〜
	end
end
绘图服务
class CreateGraph
	def call
		cache = Rails.cache.read(some_key)
		return cache if cache.present?

		# ~~~具体的な処理〜〜〜
		graph # 最後にグラフオブジェクトを返す
	end
end

但是,在这种状态下,当同时发送请求时诸如 1) 第一个摘要被复制和 2) 正在创建的图形与第一个摘要不完整等问题。去做。

因此,本文的目的是尽量防止控制器和批处理中的重叠处理。

创建一个管理状态的对象

作为一个策略,“如果进程正在运行,则采取一些措施”,所以准备一个对象来管理是否有人(包括批处理)正在运行程序,此时我会尝试。

class ProgressState
	def start
		redis.write('in_progress', '')
	end

	def finish
		redis.delete('in_progress')
	end

	def running?
		redis.exists?('in_progress')
	end

	private
	
	def redis
		Rails.cache
	end

有了这个,如果你在开始处理的时候在redis中存储了一个合适的缓存,并且每次执行处理之前检查redis中是否有缓存,似乎可以防止处理同时运行。我将尝试在控制器和批处理中实现它。

补充:

虽然叫State,但它与设计模式中的State模式不同,它的作用几乎就像一个存储库。不过这个对象的作用不是读写数据,而是管理程序的执行状态,所以我把它命名为ProgressState(如果大家还有什么好的命名建议,欢迎评论。)

批处理:退出处理

def exec
	progress_state = ProgressState.new
	return if progress_state.running?

	progress_state.start
	AggregateTablesService.new.call
	CreateGraph.new.call
	progress_state.finish
end

在批处理中,“其他东西正在运行”意味着聚合和图不再被缓存,所以我们简单地让它们离开。

控制器:使处理进入睡眠状态

def foo
	progress_state = ProgressState.new
	SleepProgressService.new(progress_state).call

	progress_state.start
	AggregateTablesService.new.call
	@something = CreateGraph.new.call
	progress_state.finish
end

我不想跳过控制器端的处理,但我想在最终将图形传递到屏幕时防止重复处理,所以我会尝试“等待其他东西正在处理”的方法。

class SleepProgressService
	SLEEP_SEC = 10
	RETRY_COUNT = 24

	def initialize(progress_state)
		@progress_state = progress_state
	end

	def call
		count = 0
		loop do
			if @progress_state.running?
				break if count > RETRY_COUNT

				count += 1
				Rails.logger.info("主要指標:別の集計処理が実行中のためスリープしました。スリープ回数:#{count}回目")
				sleep SLEEP_SEC
			else
				break
			end
		end
	end
end

问题:一旦我读取缓存,Rails 一直说“缓存存在”,即使我删除了 redis 端的数据......

让我们用上面的代码实际同时运行程序。尝试同时敲击控制器两次。然后,一屏显示聚合和图创建完成后的画面,redis中in_progress的缓存肯定会被finish方法删除,但是另一屏在休眠中会保持原样。

拿了个日志查了一下,有一个现象,@progress_state.running?即使redis端的缓存已经消失了,也持续返回true 24次。

我认为这可能是 Rails 端的 redis_cache 设置有问题,但我可以通过自己执行 Redis.new 而不使用 Rails.cache 来避免这个问题。

class ProgressState
	def initialize
		Rails.configuration.cache_store[1]
		redis_host = config[:host]
    redis_port = config[:port]
    redis_db = config[:db]
    @redis = Redis.new(host: redis_host, port: redis_port, db: redis_db)
	end

	def start
		@redis.set('in_progress', '')
	end

	def finish
		@redis.del('in_progress')
	end

	def running?
		@redis.exists('in_progress') == 1 # exists()が0/1のintで返却されるため
	end

我没什么好说的,但我想说的是,ProgressState 从一开始就被定义为一个对象,所以唯一的变化就是这个对象。

上面提到的 SleepProgressService 并没有从控制器中分离出来,因为我们想重用它,但是我们想尽可能地缩小控制器的职责范围,隔离“等待进程启动”的职责作为这项服务的责任。有。

在最后

实际代码中,实现稍微复杂一些,比如按日期和各种ID分别管理执行状态,但基本功能满足以上内容即可。

感谢您一直陪伴我们到最后。


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308628176.html

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2023-01-30
  • 2022-12-23
  • 2021-12-14
  • 2021-09-28
猜你喜欢
  • 2022-12-23
  • 2021-08-04
  • 2021-09-02
  • 2021-11-13
  • 2023-03-14
  • 2021-07-26
相关资源
相似解决方案