• 一定不完全准确,但只要你的 vacuum 执行正常,我观察下来,表越大误差越低。

  • 一个小时的误差还真赶不上这种,有了新的过滤条件还要维护,在索引里的一般误差不是很大,这种方案的前提是表大并且更新频繁,auto vacuum 就会运行的频率高,数据也就准了

    • 1 减少请求数据是在多端情况去比的
    • 2 落地页这种 gql 也不会一个请求返回所有数据,你也可以像普通 api 接口一样,为落地页单独做几个接口
    • 4 这个绝对是 gql 的优点,你普通 api 接口删了返回字段就不报错吗,gql 是可以直接 detect 出 break change 的

    摆脱 UI 驱动是 GQL 的一个优势,普通接口你需要先有页面设计,再开发接口。GQL 的话,你完全可以根据 DB、Model 直接自省出 Type,写出接口。少部分页面比如 landing page 之类,才需要和前端去沟通格式。想省时间吃饭 GQL 还真管用。

    之前的一个自动化的实践:

    • 数据库字段加 comment
    • rake 从 rails model 生成 graphql type 自动读取数据库字段注释
    • graphiql/graphql doc
    • 自动 break change 通知

  • 这个是表记录条数,不能加条件的,实际就没什么用了。

  • 快如⚡

  • cool

  • 这种方法很通用,几乎解决了我遇到了所有复杂拼接问题

  • 感觉不需要 find by sql 的

    user = User.find(x)
    done_products = user.evaluations.where(xx).select(:product_id)
    Product.select("products.*").joins("JOIN (#{done_products.to_sql}) AS dp ON dp.producct_id = products.id").where(xx).page(xx)
    
    Product.select("products.*").joins("LEFT JOIN (#{done_products.to_sql}) AS dp ON dp.producct_id = products.id").where("dp.product_id is null").page(xx)
    

    这篇文章首先提出一种稍微复杂的查询场景,在这种场景下,Rails 提供的查询方法已经无法轻易地组装查询逻辑,这个时候用原生的 SQL 语句反而简单许多。

    这个例子还有点简单,但结论我觉得恰恰是 Rails 查询封装结合 SQL 的方式在解决复杂过滤和分页需求更适合。

  • Crystal 1.0.0 Released! at 2021年04月03日

    心动了

  • u 盘

  • pagy

  • Rails 因為 mimemagic 炸了 at 2021年03月27日

    Have a great day!

  • Rails 因為 mimemagic 炸了 at 2021年03月26日

    又炸了

  • Hypercable Analytics open sourced at 2021年03月26日

    补个图

  • Rails 因為 mimemagic 炸了 at 2021年03月25日

    magic

  • Buffer Queue for Ruby at 2021年03月25日

    👍

  • Rails 因為 mimemagic 炸了 at 2021年03月25日

    昨天镜像 build 炸了 就是因为这个

  • Hypercable Analytics benchmark at 2021年03月25日

    我宁愿迁移 crystal

  • Crystal 1.0.0 Released! at 2021年03月24日

    6

  • Buffer Queue for Ruby at 2021年03月23日

  • Buffer Queue for Ruby at 2021年03月23日

    是的,没有实现 worker,但我现在的用法是和 sidekiq 一起,利用 sidekiq 的 worker 来实现,可以看上面新提交的代码

    if Sidekiq.server?
      Sidekiq.on(:startup) do
        BQ = BufferQueue.new(max_batch_size: 10, execution_interval: 100) do |batch|
          puts "bulk insert #{batch.size} records"
          Hyper::Event.import(
            EventJob::COLUMN_NAMES,
            batch.flatten.map { |attr| Hyper::Event.new(attr) },
            validate: false,
            timestamps: false
          ) unless batch.empty?
        end
      end
    end
    

    in sidekiq job:

    class EventJob
      include Sidekiq::Worker
    
      COLUMN_NAMES = Hyper::Event.column_names
    
      def perform(*args)
          result = a_lot_of_cal
          BQ.push result
        end
      end
    end
    

    效果就是先利用 sidekiq 的多线程并行处理一些计算任务,产生的结果批量插入数据库; 相比之前 sidekiq job 里单条插入会快很多。并且可以利用 activerecord import 把多条记录改成 insert into values 的形式,插入更快。

  • Buffer Queue for Ruby at 2021年03月23日

    ruby 的内置 queue 不太好用:https://bugs.ruby-lang.org/issues/9145

  • Buffer Queue for Ruby at 2021年03月23日

    效果

    [1] pry(main)> bq = BufferQueue.new(max_batch_size: 10, execution_interval: 5) do |batch|
      p batch
    end
    25.times do |i|
      bq.push(i)
    end
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
    => 25
    [2] pry(main)> [20, 21, 22, 23, 24]
    
    [2] pry(main)> quit
    shutdown ...
    
  • Buffer Queue for Ruby at 2021年03月23日

    搞了一个简单版 https://github.com/HyperCable/hypercable/pull/49/files

    # frozen_string_literal: true
    
    class BufferQueue
      attr_reader :max_batch_size, :execution_interval, :timeout_interval, :callback
      def initialize(max_batch_size: 100, execution_interval: 60, timeout_interval: 60, &callback)
        @max_batch_size = max_batch_size
        @execution_interval = execution_interval
        @timeout_interval = timeout_interval
        @queue = Queue.new
        @timer = Concurrent::TimerTask.new(execution_interval: execution_interval, timeout_interval: timeout_interval) do
          flush
        end
        @timer.execute
        @callback = callback
        at_exit { shutdown }
      end
    
      def flush
        batch = []
        max_batch_size.times do
          if not @queue.empty?
            begin
              batch << @queue.pop(true)
            rescue ThreadError
              puts "queue is empty"
              break
            end
          else
            break
          end
        end
        callback.call(batch) unless batch.empty?
      end
    
      def push(item)
        @queue << item
        if @queue.size >= max_batch_size
          flush
        end
        item
      end
    
      def shutdown
        puts "shutdown ..."
        @timer.shutdown
        flush
      end
    end
    
    
  • 这种帖子底下需要一个 warning 功能