Module: Pipeloader
- Defined in:
- lib/pipeloader.rb,
lib/pipeloader/source.rb,
lib/pipeloader/version.rb,
lib/pipeloader/ar_patch.rb,
lib/pipeloader/pipeliner.rb,
lib/pipeloader/field_exact.rb
Overview
Pipeloader makes a graphql-ruby query resolve its ActiveRecord SELECTs through a libpq pipeline: one round trip per tree level, transparently. Resolvers stay plain AR — no Futures, no dataloader.load, no field changes — because AR’s own query path is intercepted during response building.
class AppSchema < GraphQL::Schema
use Pipeloader # adds GraphQL::Dataloader (fibers) + the AR patch
end
Defined Under Namespace
Modules: ARPatch, FieldSelects, Pipeliner, Trace, TypeOptIn Classes: ProjectionExtension, Source
Constant Summary collapse
- VERSION =
"0.0.1"
Class Attribute Summary collapse
-
.field_exact ⇒ Object
Returns the value of attribute field_exact.
-
.queries ⇒ Object
Per-query stats (single-threaded; reset at the start of each query).
-
.round_trips ⇒ Object
Per-query stats (single-threaded; reset at the start of each query).
Class Method Summary collapse
-
.pipelining_supported?(conn) ⇒ Boolean
Pipelining is libpq-specific.
-
.project_columns(model, lookahead) ⇒ Object
Returns the exact column list for a model + selection, or nil meaning “can’t prove it’s safe — fetch whole rows.”.
- .reset_stats! ⇒ Object
- .use(schema) ⇒ Object
Class Attribute Details
.field_exact ⇒ Object
Returns the value of attribute field_exact.
5 6 7 |
# File 'lib/pipeloader/field_exact.rb', line 5 def field_exact @field_exact end |
.queries ⇒ Object
Per-query stats (single-threaded; reset at the start of each query).
22 23 24 |
# File 'lib/pipeloader.rb', line 22 def queries @queries end |
.round_trips ⇒ Object
Per-query stats (single-threaded; reset at the start of each query).
22 23 24 |
# File 'lib/pipeloader.rb', line 22 def round_trips @round_trips end |
Class Method Details
.pipelining_supported?(conn) ⇒ Boolean
Pipelining is libpq-specific. PostgreSQL pipelines; SQLite can’t, but the opt-in column projection is plain ActiveRecord and still applies, so SQLite is allowed with pipelining disabled. Any other adapter is unsupported.
Running SQLite un-pipelined is safe because SQLite is embedded: its queries are in-process calls with no network round trip, so there’s nothing for a dataloader or pipeline to collapse. N+1 there is just cheap local calls, not the latency amplification pipelining exists to remove.
46 47 48 49 50 51 52 53 54 |
# File 'lib/pipeloader.rb', line 46 def self.pipelining_supported?(conn) case conn.adapter_name when "PostgreSQL" then true when "SQLite" then false else raise "Pipeloader supports PostgreSQL (pipelined) and SQLite " \ "(field narrowing only); #{conn.adapter_name} is not supported." end end |
.project_columns(model, lookahead) ⇒ Object
Returns the exact column list for a model + selection, or nil meaning “can’t prove it’s safe — fetch whole rows.”
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/pipeloader/field_exact.rb', line 76 def self.project_columns(model, lookahead) columns = model.column_names needed = [model.primary_key] lookahead.selections.each do |sel| field = sel.field if field.respond_to?(:pipeloader_selects) && field.pipeloader_selects needed.concat(field.pipeloader_selects) # explicit escape hatch elsif field.owner.instance_methods(false).include?(field.resolver_method) return nil # custom resolver: opaque elsif columns.include?(field.method_str) needed << field.method_str # plain column elsif (assoc = model.reflect_on_association(field.method_str.to_sym)) needed << assoc.foreign_key if assoc.belongs_to? # FK for a belongs_to # has_many keys off the model's PK, already included else return nil # unknown accessor: opaque end end needed.compact.uniq.map(&:to_s) end |
.reset_stats! ⇒ Object
27 28 29 30 |
# File 'lib/pipeloader.rb', line 27 def self.reset_stats! self.round_trips = 0 self.queries = 0 end |