Project

bluejay

0.01
No release in over a year
A fast GraphQL engine.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

 Project Readme

bluejay-rb

Warning This project is still very early in its development and should be considered highly unstable and experimental. It is incomplete and not ready for production use.

Note In an effort to get a working proof-of-concept, documentation and test coverage has been sacrificied. This will be corrected prior to a stable release.

bluejay-rb is a GraphQL engine for Ruby written primarily in Rust through the use of magnus.

Goal

bluejay-rb's goal is to provide a lightning fast GraphQL engine with first-class integration with Sorbet. If you do not care deeply about one or both of these goals, then you should seriously consider using the excellent graphql-ruby gem, which has many more features than bluejay-rb in addition to being production-ready and much more customizable.

Benchmarks

Some benchmarks comparing the performance of bluejay-rb against graphql-ruby (with C parser) are located at /bench. The results of some of these benchmarks are included below.

Parse + Execute (Ruby 3.2, YJIT enabled)
Profiling IPS:
Warming up --------------------------------------
             graphql   158.000  i/100ms
             bluejay     1.764k i/100ms
Calculating -------------------------------------
             graphql      1.928k (± 2.7%) i/s -      9.638k in   5.003328s
             bluejay     17.899k (± 2.7%) i/s -     89.964k in   5.029921s

Comparison:
             bluejay:    17899.5 i/s
             graphql:     1927.9 i/s - 9.28x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             graphql    45.944k memsize (     2.736k retained)
                       421.000  objects (    41.000  retained)
                        12.000  strings (    11.000  retained)
             bluejay     5.256k memsize (   208.000  retained)
                        40.000  objects (     2.000  retained)
                         0.000  strings (     0.000  retained)

Comparison:
             bluejay:       5256 allocated
             graphql:      45944 allocated - 8.74x more
Parse + Execute (Ruby 3.2, YJIT disabled)
Profiling IPS:
Warming up --------------------------------------
             graphql    79.000  i/100ms
             bluejay     1.804k i/100ms
Calculating -------------------------------------
             graphql    781.860  (± 2.2%) i/s -      3.950k in   5.054411s
             bluejay     18.017k (± 1.6%) i/s -     90.200k in   5.007842s

Comparison:
             bluejay:    18016.7 i/s
             graphql:      781.9 i/s - 23.04x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             graphql    45.944k memsize (    21.752k retained)
                       421.000  objects (   197.000  retained)
                        12.000  strings (    12.000  retained)
             bluejay     5.256k memsize (     5.056k retained)
                        40.000  objects (    35.000  retained)
                         0.000  strings (     0.000  retained)

Comparison:
             bluejay:       5256 allocated
             graphql:      45944 allocated - 8.74x more
Parse + Execute with large variables (Ruby 3.2, YJIT enabled)
Profiling IPS:
Warming up --------------------------------------
             graphql   166.000  i/100ms
             bluejay     1.101k i/100ms
Calculating -------------------------------------
             graphql      1.642k (± 2.1%) i/s -      8.300k in   5.055691s
             bluejay     10.876k (± 1.8%) i/s -     55.050k in   5.063054s

Comparison:
             bluejay:    10876.3 i/s
             graphql:     1642.4 i/s - 6.62x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             graphql    97.728k memsize (    40.000  retained)
                       885.000  objects (     1.000  retained)
                         6.000  strings (     0.000  retained)
             bluejay    15.640k memsize (    80.000  retained)
                       228.000  objects (     2.000  retained)
                        15.000  strings (     1.000  retained)

Comparison:
             bluejay:      15640 allocated
             graphql:      97728 allocated - 6.25x more
Parse + Execute with large variables (Ruby 3.2, YJIT disabled)
Profiling IPS:
Warming up --------------------------------------
             graphql    79.000  i/100ms
             bluejay     1.009k i/100ms
Calculating -------------------------------------
             graphql    841.882  (± 1.5%) i/s -      4.266k in   5.068484s
             bluejay     10.163k (± 2.1%) i/s -     51.459k in   5.065688s

Comparison:
             bluejay:    10163.2 i/s
             graphql:      841.9 i/s - 12.07x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             graphql    97.688k memsize (    30.736k retained)
                       884.000  objects (   319.000  retained)
                         6.000  strings (     5.000  retained)
             bluejay    12.600k memsize (   536.000  retained)
                       152.000  objects (     6.000  retained)
                         8.000  strings (     1.000  retained)

Comparison:
             bluejay:      12600 allocated
             graphql:      97688 allocated - 7.75x more
Parse small (Ruby 3.2, YJIT enabled)
Profiling IPS:
Warming up --------------------------------------
             bluejay    39.288k i/100ms
             graphql     6.906k i/100ms
Calculating -------------------------------------
             bluejay    397.916k (± 1.4%) i/s -      2.004M in   5.036417s
             graphql     67.671k (± 4.9%) i/s -    338.394k in   5.015897s

Comparison:
             bluejay:   397915.9 i/s
             graphql:    67671.2 i/s - 5.88x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             bluejay     0.000  memsize (     0.000  retained)
                         0.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
             graphql     6.192k memsize (     2.816k retained)
                        70.000  objects (    37.000  retained)
                         6.000  strings (     6.000  retained)

Comparison:
             bluejay:          0 allocated
             graphql:       6192 allocated - Infx more
Parse small (Ruby 3.2, YJIT disabled)
Profiling IPS:
Warming up --------------------------------------
             bluejay    37.618k i/100ms
             graphql     6.034k i/100ms
Calculating -------------------------------------
             bluejay    403.347k (± 2.1%) i/s -      2.031M in   5.038540s
             graphql     59.928k (± 2.0%) i/s -    301.700k in   5.036461s

Comparison:
             bluejay:   403347.5 i/s
             graphql:    59928.3 i/s - 6.73x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             bluejay     0.000  memsize (     0.000  retained)
                         0.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
             graphql     6.192k memsize (     0.000  retained)
                        70.000  objects (     0.000  retained)
                         6.000  strings (     0.000  retained)

Comparison:
             bluejay:          0 allocated
             graphql:       6192 allocated - Infx more
Parse large (Ruby 3.2, YJIT enabled)
Profiling IPS:
Warming up --------------------------------------
             bluejay   203.000  i/100ms
             graphql    30.000  i/100ms
Calculating -------------------------------------
             bluejay      2.106k (± 1.4%) i/s -     10.556k in   5.014156s
             graphql    308.161  (± 1.9%) i/s -      1.560k in   5.064324s

Comparison:
             bluejay:     2105.7 i/s
             graphql:      308.2 i/s - 6.83x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             bluejay     0.000  memsize (     0.000  retained)
                         0.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
             graphql     1.425M memsize (   556.448k retained)
                        16.001k objects (     7.541k retained)
                        50.000  strings (    50.000  retained)

Comparison:
             bluejay:          0 allocated
             graphql:    1425400 allocated - Infx more
Parse large (Ruby 3.2, YJIT disabled)
Profiling IPS:
Warming up --------------------------------------
             bluejay   208.000  i/100ms
             graphql    27.000  i/100ms
Calculating -------------------------------------
             bluejay      2.098k (± 1.6%) i/s -     10.608k in   5.057106s
             graphql    274.835  (± 1.8%) i/s -      1.377k in   5.011880s

Comparison:
             bluejay:     2098.2 i/s
             graphql:      274.8 i/s - 7.63x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             bluejay     0.000  memsize (     0.000  retained)
                         0.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
             graphql     1.425M memsize (     0.000  retained)
                        16.001k objects (     0.000  retained)
                        50.000  strings (     0.000  retained)

Comparison:
             bluejay:          0 allocated
             graphql:    1425400 allocated - Infx more
Parse + Validate (Ruby 3.2, YJIT enabled)
Profiling IPS:
Warming up --------------------------------------
             graphql   504.000  i/100ms
             bluejay     5.370k i/100ms
Calculating -------------------------------------
             graphql      4.991k (± 6.2%) i/s -     25.200k in   5.073307s
             bluejay     45.612k (±11.5%) i/s -    225.540k in   5.057627s

Comparison:
             bluejay:    45612.5 i/s
             graphql:     4991.1 i/s - 9.14x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             graphql    33.392k memsize (    12.200k retained)
                       383.000  objects (   152.000  retained)
                        17.000  strings (    13.000  retained)
             bluejay    40.000  memsize (    40.000  retained)
                         1.000  objects (     1.000  retained)
                         0.000  strings (     0.000  retained)

Comparison:
             bluejay:         40 allocated
             graphql:      33392 allocated - 834.80x more
Parse + Validate (Ruby 3.2, YJIT disabled)
Profiling IPS:
Warming up --------------------------------------
             graphql   299.000  i/100ms
             bluejay     4.726k i/100ms
Calculating -------------------------------------
             graphql      2.953k (± 5.0%) i/s -     14.950k in   5.075697s
             bluejay     51.904k (± 4.0%) i/s -    259.930k in   5.016499s

Comparison:
             bluejay:    51904.0 i/s
             graphql:     2953.1 i/s - 17.58x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             graphql    33.304k memsize (    12.320k retained)
                       381.000  objects (   155.000  retained)
                        15.000  strings (    13.000  retained)
             bluejay    40.000  memsize (    40.000  retained)
                         1.000  objects (     1.000  retained)
                         0.000  strings (     0.000  retained)

Comparison:
             bluejay:         40 allocated
             graphql:      33304 allocated - 832.60x more
Schema dump (Ruby 3.2, YJIT enabled)
Profiling IPS:
Warming up --------------------------------------
             graphql   189.000  i/100ms
             bluejay     4.228k i/100ms
Calculating -------------------------------------
             graphql      1.929k (± 7.3%) i/s -      9.639k in   5.026688s
             bluejay     43.632k (± 4.6%) i/s -    219.856k in   5.050598s

Comparison:
             bluejay:    43631.9 i/s
             graphql:     1929.2 i/s - 22.62x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             graphql    77.788k memsize (     0.000  retained)
                       755.000  objects (     0.000  retained)
                        50.000  strings (     0.000  retained)
             bluejay   699.000  memsize (     0.000  retained)
                         1.000  objects (     0.000  retained)
                         1.000  strings (     0.000  retained)

Comparison:
             bluejay:        699 allocated
             graphql:      77788 allocated - 111.28x more
Schema dump (Ruby 3.2, YJIT disabled)
Profiling IPS:
Warming up --------------------------------------
             graphql   157.000  i/100ms
             bluejay     4.662k i/100ms
Calculating -------------------------------------
             graphql      1.562k (± 1.7%) i/s -      7.850k in   5.027157s
             bluejay     45.335k (± 3.3%) i/s -    228.438k in   5.044407s

Comparison:
             bluejay:    45335.1 i/s
             graphql:     1561.9 i/s - 29.02x  slower

Profiling Ruby memory allocations:
Calculating -------------------------------------
             graphql    77.884k memsize (   675.000  retained)
                       755.000  objects (    10.000  retained)
                        50.000  strings (     7.000  retained)
             bluejay   699.000  memsize (     0.000  retained)
                         1.000  objects (     0.000  retained)
                         1.000  strings (     0.000  retained)

Comparison:
             bluejay:        699 allocated
             graphql:      77884 allocated - 111.42x more

To update the benchmark report above, run rake bench:doc.

Installation

Install the gem and add to the application's Gemfile by executing:

$ bundle add bluejay

If bundler is not being used to manage dependencies, install the gem by executing:

$ gem install bluejay

Usage

See an example in the /example directory. Note the usage of Tapioca RBI generation for the DSL at /example/sorbet/rbi/dsl/graph

Development

After checking out the repo, run bin/setup to install dependencies and rake compile to compile the Rust extension. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install.

To release a new version, update the version number in version.rb, and push that to main. Then, create a new release in the GitHub UI with a new tag equal to the version number in version.rb prefixed with v. This will trigger the release workflow which will publish the new version with pre-built binaries.