File: parallelism.md

package info (click to toggle)
ruby-graphql 2.5.19-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 13,868 kB
  • sloc: ruby: 80,420; ansic: 1,808; yacc: 845; javascript: 480; makefile: 6
file content (105 lines) | stat: -rw-r--r-- 2,887 bytes parent folder | download | duplicates (2)
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
---
layout: guide
search: true
section: Dataloader
title: Manual Parallelism
desc: Yield to Dataloader after starting work
index: 7
---

You can coordinate with {{ "GraphQL::Dataloader" | api_doc }} to run tasks in the background. To do this, call `dataloader.yield` inside `Source#fetch` after kicking off your task. For example:

```ruby
def fetch(ids)
  # somehow queue up a background query,
  # see examples below
  future_result = async_query_for(ids)
  # return control to the dataloader
  dataloader.yield
  # dataloader will come back here
  # after calling other sources,
  # now wait for the value
  future_result.value
end
```

_Alternatively, you can use {% internal_link "AsyncDataloader", "/dataloader/async_dataloader" %} to automatically background I/O inside `Source#fetch` calls._

## Example: Rails load_async

You can use Rails's `load_async` method to load `ActiveRecord::Relation`s in the background. For example:

```ruby
class Sources::AsyncRelationSource < GraphQL::Dataloader::Source
  def fetch(relations)
    relations.each(&:load_async) # start loading them in the background
    dataloader.yield # hand back to GraphQL::Dataloader
    relations.each(&:load) # now, wait for the result, returning the now-loaded relation
  end
end
```

You could call that source from a GraphQL field method:

```ruby
field :direct_reports, [Person]

def direct_reports
  # prepare an ActiveRecord::Relation:
  direct_reports = Person.where(manager: object)
  # pass it off to the source:
  dataloader
    .with(Sources::AsyncRelationSource)
    .load(direct_reports)
end
```

## Example: Rails async calculations

In a Dataloader source, you can run Rails async calculations in the background while other work continues. For example:

```ruby
class Sources::DirectReportsCount < GraphQL::Dataloader::Source
  def fetch(users)
    # Start the queries in the background:
    promises = users.map { |u| u.direct_reports.async_count }
    # Return to GraphQL::Dataloader:
    dataloader.yield
    # Now return the results, waiting if necessary:
    promises.map(&:value)
  end
end
```

Which could be used in a GraphQL field:

```ruby
field :direct_reports_count, Int

def direct_reports_count
  dataloader.with(Sources::DirectReportsCount).load(object)
end
```

## Example: Concurrent::Future

You could use `concurrent-ruby` to put work in a background thread. For example, using `Concurrent::Future`:

```ruby
class Sources::ExternalDataSource < GraphQL::Dataloader::Source
  def fetch(urls)
    # Start some I/O-intensive work:
    futures = urls.map do |url|
      Concurrent::Future.execute {
        # Somehow fetch and parse data:
        get_remote_json(url)
      }
    end
    # Yield back to GraphQL::Dataloader:
    dataloader.yield
    # Dataloader has done what it can,
    # so now return the value, waiting if necessary:
    futures.map(&:value)
  end
end
```