Adding dry-transaction middleware via dry-system

We are starting to use dry-transaction for top-level GraphQL field resolvers: if you make a GraphQL query (via the graphql gem) it will package up the request data and fire off a transaction, looking up the actual transaction object in the dry-system application container. This works fine on its own.

We have a couple of cases where we’d like to wrap this whole thing in some additional logic. We want to measure the elapsed time for the whole transaction; we want to have consistent logging of successes and failures; in some cases we want to rewrite failures to have generic error messages. My instinct is that each of these could be “middleware”, in the same sense as Rack middleware and HTTP-stack equivalents in other languages:

class TimingMiddleware
  extend Dry::Initializer
  param :transaction

  def call(*args)
    start = Time.now
    transaction.call(*args)
    dt = Time.now - start
    puts "transaction took #{dt} seconds"
  end
end

If we took this approach, and we’re otherwise using the dry-system auto-registration, is there a good way to wrap the imported transaction objects? It’s reasonable to assume all transaction objects will start with the same dry-container name, be in a consistent file path, and be in some common containing Ruby module.

I also considered writing this middleware as “around” steps and manually adding it to each transaction, and adding it in the GraphQL-to-dry-transaction adapter instead. I’m not sure if either of those approaches would be architecturally better.

Hi @dmaze, dry-system may actually have some things that could help with this (or at least get you moving in the right direction). For example, it ships with monitoring and decorate plugins, which you can see working in their respective spec files: decorate_spec and monitor_spec.

The monitoring plugin may come closes to your needs, since it already broadcasts events for methods calls.

However, I think we still fall short in a couple of ways:

  1. The monitoring plugin’s Proxy class doesn’t capture and broadcast the elapsed time for monitored method calls
  2. The monitoring plugin needs to be activated for one container registration at a time. It would be much nicer if we could pass a key pattern (e.g. "*.transactions.*") to tell it to monitor a batch of matching container entries

I feel like both of these would be relatively straightforward changes to make (so I’d love it if you felt like contributing!). But in any case, hopefully this gets you moving in the right direction for your immediate requirements.

1 Like