Module: Datadog::RailsCachePatcher

Includes:
Patcher
Defined in:
lib/ddtrace/contrib/rails/core_extensions.rb

Overview

RailsCachePatcher contains function to patch Rails caching libraries.

Class Method Summary collapse

Methods included from Patcher

included

Methods included from Patcher::CommonMethods

#do_once, #without_warnings

Class Method Details

.cache_store_class(k) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/ddtrace/contrib/rails/core_extensions.rb', line 214

def cache_store_class(k)
  # When Redis is used, we can't only patch Cache::Store as it is
  # Cache::RedisStore, a sub-class of it that is used, in practice.
  # We need to do a per-method monkey patching as some of them might
  # be redefined, and some of them not. The latest version of redis-activesupport
  # redefines write but leaves untouched read and delete:
  # https://github.com/redis-store/redis-activesupport/blob/master/lib/active_support/cache/redis_store.rb
  c = if defined?(::ActiveSupport::Cache::RedisStore) &&
         ::ActiveSupport::Cache::RedisStore.instance_methods(false).include?(k)
        ::ActiveSupport::Cache::RedisStore
      else
        ::ActiveSupport::Cache::Store
      end
  c
end

.patch_cache_storeObject



204
205
206
207
208
209
210
211
212
# File 'lib/ddtrace/contrib/rails/core_extensions.rb', line 204

def patch_cache_store
  do_once(:patch_cache_store) do
    patch_cache_store_read
    patch_cache_store_fetch
    patch_cache_store_write
    patch_cache_store_delete
    reload_cache_store
  end
end

.patch_cache_store_deleteObject



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/ddtrace/contrib/rails/core_extensions.rb', line 311

def patch_cache_store_delete
  do_once(:patch_cache_store_delete) do
    cache_store_class(:delete).class_eval do
      alias_method :delete_without_datadog, :delete
      def delete(*args, &block)
        payload = {
          action: 'DELETE',
          key: args[0],
          tracing_context: {}
        }

        begin
          # process and catch cache exceptions
          Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
          delete_without_datadog(*args, &block)
        rescue Exception => e
          payload[:exception] = [e.class.name, e.message]
          payload[:exception_object] = e
          raise e
        end
      ensure
        Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
      end
    end
  end
end

.patch_cache_store_fetchObject



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/ddtrace/contrib/rails/core_extensions.rb', line 257

def patch_cache_store_fetch
  do_once(:patch_cache_store_fetch) do
    cache_store_class(:fetch).class_eval do
      alias_method :fetch_without_datadog, :fetch
      def fetch(*args, &block)
        payload = {
          action: 'GET',
          key: args[0],
          tracing_context: {}
        }

        begin
          # process and catch cache exceptions
          Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
          fetch_without_datadog(*args, &block)
        rescue Exception => e
          payload[:exception] = [e.class.name, e.message]
          payload[:exception_object] = e
          raise e
        end
      ensure
        Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
      end
    end
  end
end

.patch_cache_store_readObject



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/ddtrace/contrib/rails/core_extensions.rb', line 230

def patch_cache_store_read
  do_once(:patch_cache_store_read) do
    cache_store_class(:read).class_eval do
      alias_method :read_without_datadog, :read
      def read(*args, &block)
        payload = {
          action: 'GET',
          key: args[0],
          tracing_context: {}
        }

        begin
          # process and catch cache exceptions
          Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
          read_without_datadog(*args, &block)
        rescue Exception => e
          payload[:exception] = [e.class.name, e.message]
          payload[:exception_object] = e
          raise e
        end
      ensure
        Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
      end
    end
  end
end

.patch_cache_store_writeObject



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/ddtrace/contrib/rails/core_extensions.rb', line 284

def patch_cache_store_write
  do_once(:patch_cache_store_write) do
    cache_store_class(:write).class_eval do
      alias_method :write_without_datadog, :write
      def write(*args, &block)
        payload = {
          action: 'SET',
          key: args[0],
          tracing_context: {}
        }

        begin
          # process and catch cache exceptions
          Datadog::Contrib::Rails::ActiveSupport.start_trace_cache(payload)
          write_without_datadog(*args, &block)
        rescue Exception => e
          payload[:exception] = [e.class.name, e.message]
          payload[:exception_object] = e
          raise e
        end
      ensure
        Datadog::Contrib::Rails::ActiveSupport.finish_trace_cache(payload)
      end
    end
  end
end

.reload_cache_storeObject



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/ddtrace/contrib/rails/core_extensions.rb', line 338

def self.reload_cache_store
  redis = Datadog.registry[:redis]
  return unless redis && redis.patched?

  return unless defined?(::ActiveSupport::Cache::RedisStore) &&
                defined?(::Rails.cache) &&
                ::Rails.cache.is_a?(::ActiveSupport::Cache::RedisStore)

  Tracer.log.debug('Reloading redis cache store')

  # backward compatibility: Rails 3.x doesn't have `cache=` method
  cache_store = ::Rails.configuration.cache_store
  cache_instance = ::ActiveSupport::Cache.lookup_store(cache_store)
  if ::Rails::VERSION::MAJOR.to_i == 3
    silence_warnings { Object.const_set 'RAILS_CACHE', cache_instance }
  elsif ::Rails::VERSION::MAJOR.to_i > 3
    ::Rails.cache = cache_instance
  end
end