kasket 4.13.0 → 4.14.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c1c83230c5c06c292c4dd62d839372d06a54a763ed7dfd0ea5ed339fb57913a
4
- data.tar.gz: 96df87b7c7ee9f8f19e593b3bc3bcbad257d928e0cdc4e0b3f5b22afed00b9c6
3
+ metadata.gz: eb2a2f680632f3028aff279d4bfa0c04529c684aab4721c5cffed23ffad3ef2c
4
+ data.tar.gz: 6ec2a5b844e162f5f26325de2421ebbfecdc70d4f0340174a420b2d653376891
5
5
  SHA512:
6
- metadata.gz: 1ced9df5e050870ed7446bcf1b2e469fa2954ce1e7afcd67b799b14bb25a7323690242ca5ebb097c8d7df7f5c0c70317a72f292c202bebcc189b7afb64e905d1
7
- data.tar.gz: aef1d32b14c4d6c3526b2b9093fec72f2c1998c04bebcf40861a30a1226234e4a70976168e605358fb452ed9152e49a7d69564c2fd7583541056c66cbe7fc8f0
6
+ metadata.gz: a0311c7881f4182492caa27097ac59fce33f790257af6d6d80f069aa1c01d2677dcbcaa9c4c10f94a9b0cd54ff3b6af528986a271a1dad798b8264d979834940
7
+ data.tar.gz: ec1d22cc3255bb52f1a8796948d7805798fa3282fb58235dfc003c2dfd29cadaafb6140fd0841a38071782ffd6dad7608b7e3e697ad6b50a4bde6a161e55fc0a
data/README.md CHANGED
@@ -48,6 +48,19 @@ semantics instead. In this mode, the model will be updated in the cache as well
48
48
  Kasket.setup(write_through: true)
49
49
  ```
50
50
 
51
+ #### Events Callback
52
+
53
+ You can configure a callable object to listen to events. This can be useful to emit metrics and observe Kasket's behaviour.
54
+
55
+ ```ruby
56
+ Kasket.setup(events_callback: -> (event, ar_klass) do
57
+ MyMetrics.increase_some_counter("kasket.#{event}", tags: ["table:#{ar_klass.table_name}"])
58
+ end)
59
+ ```
60
+
61
+ The following events are emitted:
62
+ * `"cache_hit"`, when Kasket has found some record's data in the cache, which can be returned.
63
+
51
64
  ## Configuring caching of your models
52
65
 
53
66
  You can configure Kasket for any ActiveRecord model, and subclasses will automatically inherit the caching
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kasket
4
+ # Interface to the internal instrumentation event.
5
+ module Events
6
+ class << self
7
+ # Invokes the configured events callback, if provided.
8
+ #
9
+ # The callback behaves like a listener, and receives the same arguments
10
+ # that are passed to this `report` method.
11
+ #
12
+ # @param [String] event the type of event being instrumented.
13
+ # @param [class] ar_klass the ActiveRecord::Base subclass that the event
14
+ # refers to.
15
+ #
16
+ # @return [nil]
17
+ #
18
+ def report(event, ar_klass)
19
+ return unless fn
20
+
21
+ fn.call(event, ar_klass)
22
+ nil
23
+ end
24
+
25
+ private
26
+
27
+ def fn
28
+ return @fn if defined?(@fn)
29
+
30
+ @fn = Kasket::CONFIGURATION[:events_callback]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -36,8 +36,15 @@ module Kasket
36
36
  result_set = if value.is_a?(TrueClass)
37
37
  find_by_sql_without_kasket(sql, binds, *restargs, **kwargs, &blk)
38
38
  elsif value.is_a?(Array)
39
+ # The data from the Kasket cache is a list of keys to other Kasket entries.
40
+ # This usually happens when we're trying to load a collection association,
41
+ # e.g. a list of comments using their post_id in the query.
42
+ # Do not report a cache hit yet, and defer it until we've verified that at
43
+ # least one of the retrieved keys is actually in the cache.
39
44
  filter_pending_records(find_by_sql_with_kasket_on_id_array(value))
40
45
  else
46
+ # Direct cache hit for the key.
47
+ Events.report("cache_hit", self)
41
48
  filter_pending_records(Array.wrap(value).collect { |record| instantiate(record.dup, &blk) })
42
49
  end
43
50
 
@@ -60,6 +67,9 @@ module Kasket
60
67
  key_attributes_map = Kasket.cache.read_multi(*keys)
61
68
 
62
69
  found_keys, missing_keys = keys.partition {|k| key_attributes_map[k] }
70
+ # Only report a cache hit if at least some keys were found in the cache.
71
+ Events.report("cache_hit", self) if found_keys.any?
72
+
63
73
  found_keys.each {|k| key_attributes_map[k] = instantiate(key_attributes_map[k].dup, &blk) }
64
74
  key_attributes_map.merge!(missing_records_from_db(missing_keys))
65
75
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Kasket
3
- VERSION = '4.13.0'
3
+ VERSION = '4.14.1'
4
4
  class Version
5
5
  MAJOR = Kasket::VERSION.split('.')[0]
6
6
  MINOR = Kasket::VERSION.split('.')[1]
data/lib/kasket.rb CHANGED
@@ -14,21 +14,35 @@ module Kasket
14
14
  autoload :Visitor, 'kasket/visitor'
15
15
  autoload :SelectManagerMixin, 'kasket/select_manager_mixin'
16
16
  autoload :RelationMixin, 'kasket/relation_mixin'
17
+ autoload :Events, 'kasket/events'
17
18
 
18
19
  CONFIGURATION = { # rubocop:disable Style/MutableConstant
19
20
  max_collection_size: 100,
20
21
  write_through: false,
21
- default_expires_in: nil
22
+ default_expires_in: nil,
23
+ events_callback: nil,
22
24
  }
23
25
 
24
26
  module_function
25
27
 
28
+ # Configure Kasket.
29
+ #
30
+ # @param [Hash] options the configuration options for Kasket.
31
+ # @option options [Integer] :max_collection_size max size limit for a cacheable
32
+ # collection of records.
33
+ # @option options [Boolean] :write_through
34
+ # @option options [Integer, nil] :default_expires_in the cache TTL.
35
+ # @option options [#call] :events_callback a callable object used to instrument
36
+ # Kasket operations. It is invoked with two arguments: the name of the event,
37
+ # as a String, and the Klass of the ActiveRecord model the event is about.
38
+ #
26
39
  def setup(options = {})
27
40
  return if ActiveRecord::Base.respond_to?(:has_kasket)
28
41
 
29
42
  CONFIGURATION[:max_collection_size] = options[:max_collection_size] if options[:max_collection_size]
30
43
  CONFIGURATION[:write_through] = options[:write_through] if options[:write_through]
31
44
  CONFIGURATION[:default_expires_in] = options[:default_expires_in] if options[:default_expires_in]
45
+ CONFIGURATION[:events_callback] = options[:events_callback] if options[:events_callback]
32
46
 
33
47
  ActiveRecord::Base.extend(Kasket::ConfigurationMixin)
34
48
  ActiveRecord::Relation.include(Kasket::RelationMixin)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kasket
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.13.0
4
+ version: 4.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mick Staugaard
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-04-21 00:00:00.000000000 Z
12
+ date: 2024-01-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -20,7 +20,7 @@ dependencies:
20
20
  version: '5.1'
21
21
  - - "<"
22
22
  - !ruby/object:Gem::Version
23
- version: '7'
23
+ version: '7.1'
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
@@ -30,7 +30,7 @@ dependencies:
30
30
  version: '5.1'
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
- version: '7'
33
+ version: '7.1'
34
34
  description: puts a cap on your queries
35
35
  email:
36
36
  - mick@zendesk.com
@@ -42,6 +42,7 @@ files:
42
42
  - lib/kasket.rb
43
43
  - lib/kasket/configuration_mixin.rb
44
44
  - lib/kasket/dirty_mixin.rb
45
+ - lib/kasket/events.rb
45
46
  - lib/kasket/query_parser.rb
46
47
  - lib/kasket/read_mixin.rb
47
48
  - lib/kasket/relation_mixin.rb
@@ -51,7 +52,7 @@ files:
51
52
  - lib/kasket/write_mixin.rb
52
53
  homepage: http://github.com/zendesk/kasket
53
54
  licenses:
54
- - Apache License Version 2.0
55
+ - Apache-2.0
55
56
  metadata: {}
56
57
  post_install_message:
57
58
  rdoc_options: []
@@ -61,14 +62,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
61
62
  requirements:
62
63
  - - ">="
63
64
  - !ruby/object:Gem::Version
64
- version: 2.6.0
65
+ version: '2.7'
65
66
  required_rubygems_version: !ruby/object:Gem::Requirement
66
67
  requirements:
67
68
  - - ">="
68
69
  - !ruby/object:Gem::Version
69
70
  version: '0'
70
71
  requirements: []
71
- rubygems_version: 3.0.3.1
72
+ rubygems_version: 3.5.3
72
73
  signing_key:
73
74
  specification_version: 4
74
75
  summary: A write back caching layer on active record