Class: Trifle::Stats::Driver::Mysql

Inherits:
Object
  • Object
show all
Includes:
Mixins::Packer
Defined in:
lib/trifle/stats/driver/mysql.rb

Overview

rubocop:disable Metrics/ClassLength

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixins::Packer

included

Constructor Details

#initialize(client, table_name: 'trifle_stats', joined_identifier: :full, ping_table_name: nil, system_tracking: true) ⇒ Mysql

rubocop:disable Layout/LineLength



14
15
16
17
18
19
20
21
# File 'lib/trifle/stats/driver/mysql.rb', line 14

def initialize(client, table_name: 'trifle_stats', joined_identifier: :full, ping_table_name: nil, system_tracking: true) # rubocop:disable Layout/LineLength
  @client = client
  @table_name = table_name
  @ping_table_name = ping_table_name || "#{table_name}_ping"
  @joined_identifier = self.class.normalize_joined_identifier(joined_identifier)
  @system_tracking = system_tracking
  @separator = '::'
end

Instance Attribute Details

#clientObject

Returns the value of attribute client.



12
13
14
# File 'lib/trifle/stats/driver/mysql.rb', line 12

def client
  @client
end

#ping_table_nameObject

Returns the value of attribute ping_table_name.



12
13
14
# File 'lib/trifle/stats/driver/mysql.rb', line 12

def ping_table_name
  @ping_table_name
end

#separatorObject (readonly)

Returns the value of attribute separator.



61
62
63
# File 'lib/trifle/stats/driver/mysql.rb', line 61

def separator
  @separator
end

#table_nameObject

Returns the value of attribute table_name.



12
13
14
# File 'lib/trifle/stats/driver/mysql.rb', line 12

def table_name
  @table_name
end

Class Method Details

.normalize_joined_identifier(value) ⇒ Object

rubocop:enable Metrics/MethodLength



134
135
136
137
138
139
140
141
# File 'lib/trifle/stats/driver/mysql.rb', line 134

def self.normalize_joined_identifier(value)
  case value
  when nil, :full, 'full', :partial, 'partial'
    value.nil? ? nil : value.to_sym
  else
    raise ArgumentError, 'joined_identifier must be nil, :full, "full", :partial, or "partial"'
  end
end

.quote_identifier(identifier) ⇒ Object



143
144
145
# File 'lib/trifle/stats/driver/mysql.rb', line 143

def self.quote_identifier(identifier)
  "`#{identifier.to_s.gsub('`', '``')}`"
end

.setup!(client, table_name: 'trifle_stats', joined_identifier: :full, ping_table_name: nil) ⇒ Object

rubocop:disable Metrics/MethodLength



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/trifle/stats/driver/mysql.rb', line 23

def self.setup!(client, table_name: 'trifle_stats', joined_identifier: :full, ping_table_name: nil) # rubocop:disable Metrics/MethodLength
  ping_table_name ||= "#{table_name}_ping"
  identifier_mode = normalize_joined_identifier(joined_identifier)
  quoted_table_name = quote_identifier(table_name)
  quoted_ping_table_name = quote_identifier(ping_table_name)

  case identifier_mode
  when :full
    client.query(<<~SQL)
      CREATE TABLE IF NOT EXISTS #{quoted_table_name}
      (`key` VARCHAR(255) PRIMARY KEY, `data` JSON NOT NULL)
    SQL
  when :partial
    client.query(<<~SQL)
      CREATE TABLE IF NOT EXISTS #{quoted_table_name}
      (`key` VARCHAR(255) NOT NULL, `at` DATETIME(6) NOT NULL, `data` JSON NOT NULL, PRIMARY KEY (`key`, `at`))
    SQL
  else
    client.query(<<~SQL)
      CREATE TABLE IF NOT EXISTS #{quoted_table_name}
      (`key` VARCHAR(255) NOT NULL, `granularity` VARCHAR(255) NOT NULL, `at` DATETIME(6) NOT NULL, `data` JSON NOT NULL, PRIMARY KEY (`key`, `granularity`, `at`))
    SQL
    client.query(<<~SQL)
      CREATE TABLE IF NOT EXISTS #{quoted_ping_table_name}
      (`key` VARCHAR(255) PRIMARY KEY, `at` DATETIME(6) NOT NULL, `data` JSON NOT NULL)
    SQL
  end
end

Instance Method Details

#descriptionObject



52
53
54
55
56
57
58
59
# File 'lib/trifle/stats/driver/mysql.rb', line 52

def description
  mode = if @joined_identifier == :full
           'J'
         else
           @joined_identifier == :partial ? 'P' : 'S'
         end
  "#{self.class.name}(#{mode})"
end

#get(keys:) ⇒ Object



97
98
99
100
101
# File 'lib/trifle/stats/driver/mysql.rb', line 97

def get(keys:)
  identifiers = keys.map { |key| identifier_for(key) }
  data = get_all(identifiers: identifiers)
  identifiers.map { |identifier| self.class.unpack(hash: data.fetch(lookup_key_for(identifier), {})) }
end

#inc(keys:, values:, count: 1, tracking_key: nil) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/trifle/stats/driver/mysql.rb', line 73

def inc(keys:, values:, count: 1, tracking_key: nil)
  data = self.class.pack(hash: values)
  with_transaction(client) do |connection|
    keys.each do |key|
      identifier = identifier_for(key)
      query, args = inc_query(identifier: identifier, data: data)
      execute_prepared(connection, query, args)
      track_system_data(connection, key, count, tracking_key)
    end
  end
end

#ping(key:, values:) ⇒ Object



103
104
105
106
107
108
109
110
111
112
# File 'lib/trifle/stats/driver/mysql.rb', line 103

def ping(key:, values:)
  return [] if @joined_identifier

  data = self.class.pack(hash: { data: values, at: key.at })
  query, args = ping_query(key: key.key, at: key.at, data: data)

  with_transaction(client) do |connection|
    execute_prepared(connection, query, args)
  end
end

#scan(key:) ⇒ Object

rubocop:disable Metrics/MethodLength



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/trifle/stats/driver/mysql.rb', line 115

def scan(key:)
  return [] if @joined_identifier

  query = <<~SQL
    SELECT `at`, CAST(`data` AS CHAR) AS data
    FROM #{self.class.quote_identifier(ping_table_name)}
    WHERE `key` = ?
    ORDER BY `at` DESC
    LIMIT 1
  SQL
  result = execute_prepared(client, query, [key.key]).first
  return [] if result.nil?

  [parse_time_value(result['at']), self.class.unpack(hash: JSON.parse(result['data']))]
rescue JSON::ParserError
  []
end

#set(keys:, values:, count: 1, tracking_key: nil) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
# File 'lib/trifle/stats/driver/mysql.rb', line 85

def set(keys:, values:, count: 1, tracking_key: nil)
  data = self.class.pack(hash: values)
  with_transaction(client) do |connection|
    keys.each do |key|
      identifier = identifier_for(key)
      query, args = set_query(identifier: identifier, data: data)
      execute_prepared(connection, query, args)
      track_system_data(connection, key, count, tracking_key)
    end
  end
end

#system_data_for(key:, count: 1, tracking_key: nil) ⇒ Object



68
69
70
71
# File 'lib/trifle/stats/driver/mysql.rb', line 68

def system_data_for(key:, count: 1, tracking_key: nil)
  tracking_key ||= key.key
  self.class.pack(hash: { count: count, keys: { tracking_key => count } })
end

#system_identifier_for(key:) ⇒ Object



63
64
65
66
# File 'lib/trifle/stats/driver/mysql.rb', line 63

def system_identifier_for(key:)
  key = Nocturnal::Key.new(key: '__system__key__', granularity: key.granularity, at: key.at)
  identifier_for(key)
end