有没有办法在不查询/减少查询的情况下了解数据库更新?

时间:2017-10-13 07:56:55

标签: ruby-on-rails ajax postgresql ruby-on-rails-5 actioncable

我遇到了一个奇怪的问题,寻找可以到达的最佳解决方案。我正在开发一个rails应用程序,它显示来自另一个应用程序(nodejs)正在使用的公共数据库的数据。所有CRUD操作都发生在另一个平台上。在rails应用程序中,我们只查询并显示数据。

在rails app中,我需要自动更新视图而不刷新。例如

def index
    @states = State.page(params[:state_page])
    @level_one_companies = Company.includes(:state)
                                  .where(level: 1)
                                  .order('created_at DESC').limit(20)

    @level_two_companies = Company.includes(:state)
                                  .where(level: 2)
                                  .order('created_at DESC').limit(20)
end

在索引页面上,我将为每个表格提供表格,当新数据添加到州(或)第1级(或)第2级公司时,我需要刷新表格。

我知道我可以通过两种方式自动更新视图,例如

  1. 行动电缆。
  2. 使用Jquery以一定时间间隔汇总数据。
  3. 通常在使用Action Cable时,我们将在db中创建记录后从服务器广播数据(在create action(或)after_save回调模型之后的.save之后)。但是,我没有通过rails app创建任何记录。

    我的第一个问题是,在这种情况下有没有办法使用动作电缆?。

    所以我选择了第二个选项并且工作正常。但它每隔X秒就会进行过多的db调用。 有没有办法减少查询以更新视图? 我能和这里一起去的最佳方式是什么?任何帮助高度赞赏。谢谢。

2 个答案:

答案 0 :(得分:3)

如果您的代码设置正确,则您将postgres用作数据库。

postgres提供了一个发布 - 订阅机制,您可以将其与action-cable结合使用,以便收听数据库中的更改。

this gist中,您可以找到带有server-sent-events的postgres-pubsub的示例。将其转换为动作电缆兼容代码应该很简单。

答案 1 :(得分:0)

您可以在桌面上创建触发器(创建/更新/删除)以在"频道"上触发通知,并且您可以收听所述频道的事件。我使用socketcluster,听取工作人员的意见,并向消费者(浏览器和移动应用程序)广播。

首先创建触发器:

CREATE FUNCTION deletes_notify_trigger() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
DECLARE
BEGIN
  PERFORM pg_notify('deletes_channel',  ('DELETED' || ';;' || OLD.id )::text );
  RETURN new;
END;
$$;

CREATE TRIGGER deletes_trigger AFTER DELETE ON events FOR EACH ROW EXECUTE PROCEDURE deletes_notify_trigger();

你可以在正在广播的数据包中添加你想要的任何东西,在我的情况下我只需要记录的ID。对于创建和更新,您可以发送完整行或仅发送一些选定列。您也可以在PG 9.2(我认为)和更高版本中将其作为JSON发送。我使用9.1,所以我连续使用;;分隔符。

确保您的代码在查询之间最多占用10%的时间,否则如果您执行复杂的连接或更新或其他操作,您会发现性能会显着下降。您希望它尽可能简单快速,将其保持在基本操作中,并对应用程序层进行任何繁重的工作。

然后,观察者和广播给消费者(在我的情况下节点,socketcluster和pg gem,在你的情况下你可以使用JS,Python,Ruby,无论你喜欢什么)

var global_deletes = socket.subscribe('deletes_channel');
pg.connect(connectionString, function(err, client) {
      client.on('notification', function(dbmsg) {
        console.log(dbmsg.payload);
        var payload = dbmsg.payload.split(";;"); // you can use JSON instead

        if (payload[0] == "DELETED") { // when a DELETE is received...
            global_deletes.publish(dbmsg.payload);
            var tchannel = socket.subscribe('events-'+ payload[1]); // join the channel we want to broadcast
            setTimeout( () => tchannel.publish(dbmsg.payload), 50); // send the update to all consumers
            setTimeout( () => tchannel.unsubscribe(), 100);
        }
       );
    var query = client.query("LISTEN deletes_channel"); // turn on notifications from the server
});