ruby - 如何避免重复添加?

标签 ruby mongodb asynchronous mongoid

我要解决的问题

我有一个工作进程,它通过从工作人员收到的一些 JSON 消息中累积值来更改资源的属性(比如说 MyResource)。我正在尝试提出一种避免重复累积的最佳方法,即使工作进程收到两次或更多次相同的 JSON 消息也是如此。

这是我试过的

方案一

每条 JSON 消息都有一个唯一的时间戳,这取决于 JSON 消息的创建时间,我将该时间戳保存在 MyResource 上,如果 JSON 消息的时间戳值低于 MyResource 上的值,我将拒绝该消息。

问题

由于整个架构是异步的,因此消息可能以任何顺序接收,不一定与创建时的顺序相同。

解决方案2

我在 MyResource 上创建了一个新属性(比如 added_ids)。每条 JSON 消息都有一个唯一的 ID,我将该 ID 附加到 MyResource.added_ids。并且每次都为已处理的 JSON 消息累积使用的 added_ids。

问题

我正在使用 mongo 来存储 MyResource。由于每个 MyResource 的 JSON 消息很多,因此每个 MyResource 文档都开始因这个 id 数组而爆炸。在数组中查找也是一项昂贵的操作。

我在找

我正在寻找一个可以处理异步性质并且不会破坏我的 mongo 文档的答案。另外我不是在寻找一个确切的解决方案,是否有用于解决类似问题的算法/模式?我尝试使用谷歌搜索,但我不知道如何称呼这个问题来获得相关结果。

最佳答案

我认为您的第二个解决方案是正确的,但是如果您将每个 added_id 存储为自己的键值而不是数组,性能可能会更好。

逻辑非常简单:每次从队列中获取输入时,在缓存中查找是否存在该消息 ID 的条目。如果有条目,则不要累积该输入。否则,累积输入并将 key 存储在缓存中。

正如您所提到的,这种方法存在可扩展性问题,因为缓存会无限增长。要解决此问题,您可以使用具有过期和逐出功能的缓存。解决此问题的最简单方法是明确设置您编写的每个 key 的“过期时间”。这是由 Mongo、Memcached 和 Redis 支持的。

问题是,即使你在每个点都设置了“expires at”,如果负载足够大,你的缓存仍然会耗尽内存。所以你需要一个回退——当缓存内存不足时要做的事情。为此,您可以使用具有“自动驱逐”功能的缓存,这意味着它有一种算法可以在必要时删除内容。

看起来 Mongo 不支持这样的东西(它是一个具有缓存功能的数据库,而不是适当的缓存)。 Memcache 使用 LRU 算法(参见 https://github.com/memcached/memcached/wiki/UserInternals#when-are-items-evicted )。 Redis 有多种算法供您选择(请参阅 https://redis.io/topics/lru-cache )。

我要记住的另一件事是,在分布式或多线程应用程序上执行整个过程会引入竞争条件。假设您有 20 台工作机器,无论出于何种原因,它们几乎都在同一时间收到相同的消息。他们每个人都会检查缓存中的条目,但什么也找不到,因此没有一个被标记为重复。

要解决此问题,您可以对在同一台机器上运行的多个线程使用互斥锁/信号量(垂直缩放),或者如果您有多个机器(水平缩放),则可以使用“分布式锁”。参见 https://redis.io/topics/distlock

编辑

我收到一条提示,Mongo 可以使用 Capped Collections 进行自动驱逐.它仅支持 FIFO 逐出(总是首先使最旧的数据过期),这无论如何都可以满足您的需求。

关于ruby - 如何避免重复添加?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55755792/

相关文章:

ruby-on-rails - 有没有一种优雅的方法可以更正加拿大邮政编码中常见的数据输入错误?

node.js - 让 Mongoose 重新连接到 Node 并向辅助 Node 发送请求时出现问题

使用 GET 函数的 R Shiny 异步编程

Java EE 8 : No transaction in asynchronous observer (CDI 2. 0)

ruby-on-rails - rails 上的 ruby : Yielding specific views in a specific places in the layout

ruby 和引用资料。使用 fixnums

ruby - File.read() 在我的 Capistrano 任务中失败(文件*确实*存在)

mongodb - 助力车:插入后获取ID

ruby - 如何从 Mongoid 的嵌入文档中排除字段?

c# - 使用服务引用时无法将 IAsyncResult 转换为 AsyncResult