File: dataloader_spec.rb

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (118 lines) | stat: -rw-r--r-- 3,680 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# frozen_string_literal: true
require "spec_helper"

describe GraphQL::Dataloader do
  if defined?(ActiveRecord::Promise) && ENV['DATABASE'] == 'POSTGRESQL' # Rails 7.1+
    class RailsPromiseSchema < GraphQL::Schema
      class LoadAsyncSource < GraphQL::Dataloader::Source
        LOG = []
        def fetch(relations)
          relations.each do |rel|
            LOG << Time.now
            rel.load_async
          end
          dataloader.yield
          relations.each do |rel|
            rel.load
            LOG << Time.now
          end
        end
      end

      class SleepSource < GraphQL::Dataloader::Source
        def initialize(duration)
          @duration = duration
        end

        def fetch(durations)
          # puts  "[#{Time.now.to_f}] Starting #{durations}"
          promise = ::Food.async_find_by_sql("SELECT pg_sleep(#{durations.first})")
          # puts "[#{Time.now.to_f}] Yielding #{durations}"
          dataloader.yield
          # puts "[#{Time.now.to_f}] Finishing #{durations}"
          promise.value
          durations
        end
      end
      class Query < GraphQL::Schema::Object
        field :sleep, Float do
          argument :duration, Float
        end

        def sleep(duration:)
          dataloader.with(SleepSource, duration).load(duration)
        end

        field :things, Integer do
          argument :first, Integer
        end

        def things(first:)
          relation = Food
            .where(name: "Zucchini")
            .select("pg_sleep(0.3)")
            .limit(first)
          items = dataloader.with(LoadAsyncSource).load(relation)
          items.size
        end
      end

      query(Query)
      use GraphQL::Dataloader
    end

    before do
      Food.create!(name: "Zucchini")
      RailsPromiseSchema::LoadAsyncSource::LOG.clear
    end

    after do
      Food.find_by(name: "Zucchini").destroy
    end

    it "Supports async queries" do
      assert ActiveRecord::Base.connection.async_enabled?, "ActiveRecord must support real async queries"
    end

    describe "using ActiveRecord::Promise for manual parallelism" do
      it "runs queries in parallel" do
        query_str = "
        {
          s1: sleep(duration: 0.1)
          s2: sleep(duration: 0.2)
          s3: sleep(duration: 0.3)
        }"
        t1 = Time.now
        result = RailsPromiseSchema.execute(query_str)
        t2 = Time.now
        assert_equal({ "s1" => 0.1, "s2" => 0.2, "s3" => 0.3}, result["data"])
        assert_in_delta 0.3, t2 - t1, 0.05, "Sleeps were in parallel"
      end
    end

    describe "using load_async for parallelism" do
      it "runs queries in parallel" do
        query_str = "
        {
          t1: things(first: 5)
          t2: things(first: 10)
          t3: things(first: 100)
        }"
        t1 = Time.now
        result = RailsPromiseSchema.execute(query_str)
        t2 = Time.now

        load_async_1, load_async_2, load_async_3, load_1, load_2, load_3 = RailsPromiseSchema::LoadAsyncSource::LOG
        assert_in_delta load_async_1, load_async_2, 0.05, "load_async happened first"
        assert_in_delta load_async_1, load_async_3, 0.05, "the third load_async happened right after"

        assert_in_delta load_async_1, load_1, 0.35, "load came 0.3s after"
        assert_in_delta load_1, load_2, 0.05, "the second load didn't have to wait because it was already done"
        assert_in_delta load_1, load_3, 0.05, "the third load didn't have to wait because it was already done"

        assert_equal({ "t1" => 1, "t2" => 1, "t3" => 1}, result["data"])
        assert_in_delta 0.3, t2 - t1, 0.05, "Sleeps were in parallel"
      end
    end
  end
end