Skip to content

Commit a3bb944

Browse files
committed
chore: rename slow_log_check to slowlog_check since slowlog refers
to the redis command "slowlog"
0 parents  commit a3bb944

File tree

7 files changed

+209
-0
lines changed

7 files changed

+209
-0
lines changed

.bundle/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
BUNDLE_PATH: "vendor/bundle"

.ruby-gemset

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
slow_log_check

.ruby-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ruby-2.5.0

Gemfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
source 'https://rubygems.org'
2+
3+
gem 'redis'
4+
gem 'dogapi'
5+
6+
group :development do
7+
gem 'pry'
8+
end

Gemfile.lock

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
GEM
2+
remote: https://rubygems.org/
3+
specs:
4+
coderay (1.1.2)
5+
dogapi (1.40.0)
6+
multi_json
7+
method_source (1.0.0)
8+
multi_json (1.14.1)
9+
pry (0.13.1)
10+
coderay (~> 1.1)
11+
method_source (~> 1.0)
12+
redis (4.1.3)
13+
14+
PLATFORMS
15+
ruby
16+
17+
DEPENDENCIES
18+
dogapi
19+
pry
20+
redis
21+
22+
BUNDLED WITH
23+
1.17.3

inject_slow_query.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env ruby
2+
# Copyright 2020 Scribd, Inc.
3+
4+
require 'logger'
5+
require 'redis'
6+
7+
LOGGER = Logger.new($stdout)
8+
LOGGER.level = Logger::WARN
9+
10+
REDIS = Redis.new(
11+
host: ENV.fetch('REDIS_HOST'),
12+
ssl: :true
13+
)
14+
15+
SCRIPT =<<END
16+
-- From https://medium.com/@stockholmux/simulating-a-slow-command-with-node-redis-and-lua-efadbf913cd9
17+
local aTempKey = "a-temp-key"
18+
local cycles
19+
redis.call("SET",aTempKey,"1")
20+
redis.call("PEXPIRE",aTempKey, 100)
21+
for i = 0, 1500000, 1 do
22+
local apttl = redis.call("PTTL",aTempKey)
23+
cycles = i;
24+
if apttl == 0 then
25+
break;
26+
end
27+
end
28+
return cycles
29+
END
30+
31+
LOGGER.info REDIS.eval(SCRIPT)

slowlog_check.rb

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env ruby
2+
# Copyright 2020 Scribd, Inc.
3+
4+
require 'logger'
5+
require 'date'
6+
require 'redis'
7+
require 'dogapi'
8+
9+
LOGGER = Logger.new($stdout)
10+
LOGGER.level = Logger::INFO
11+
12+
REDIS = Redis.new(
13+
host: ENV.fetch('REDIS_HOST'),
14+
ssl: :true
15+
)
16+
17+
DDOG = Dogapi::Client.new(
18+
ENV.fetch('DATADOG_API_KEY'),
19+
ENV.fetch('DATADOG_APP_KEY')
20+
)
21+
22+
def log_context
23+
LOGGER.debug('## ENVIRONMENT VARIABLES')
24+
LOGGER.debug(ENV.to_a)
25+
LOGGER.debug('## EVENT')
26+
LOGGER.debug(@event)
27+
end
28+
29+
def time
30+
# DateTime because Time does not natively parse AWS CloudWatch Event time
31+
DateTime.rfc3339(@event.fetch("time", DateTime.now.rfc3339))
32+
end
33+
34+
def replication_group
35+
matches = /\w\.(?<replication_group>[\w-]+)\.\w+\.\w+\.cache\.amazonaws\.com/.match(ENV.fetch('REDIS_HOST'))
36+
if matches
37+
matches[:replication_group]
38+
else
39+
raise "Unable to parse REDIS_HOST. Is #{ENV.fetch('REDIS_HOST','NO REDIS_HOST DEFINED')} a valid elasticache endpoint?"
40+
end
41+
end
42+
43+
def last_datadog_metrics_submitted_by_me_in_the_last_day
44+
resp = DDOG.get_points(
45+
"scribd.slowlog_check.slowlog{replication_group:#{replication_group}}",
46+
Time.now - 86400,
47+
Time.now
48+
)
49+
50+
raise "Error getting last datadog metric submitted by me" unless resp[0] == "200"
51+
resp
52+
end
53+
54+
def last_datadog_metric
55+
Time.at(
56+
last_datadog_metrics_submitted_by_me_in_the_last_day[1]
57+
.fetch("series")
58+
.first
59+
.fetch("pointlist")
60+
.map {|x| x[0]}
61+
.max
62+
.to_i / 1000
63+
)
64+
end
65+
66+
def last_time_submitted
67+
return @last_time_submitted if defined? @last_time_submitted
68+
@last_time_submitted = last_datadog_metric
69+
end
70+
71+
def emit_point(time, value, tags)
72+
LOGGER.info "Sending slowlog entry: #{value}µs executing #{tags[:command]} at #{time}."
73+
resp = DDOG.emit_points(
74+
'redis.slowlog.micros.avg',
75+
[[time, value]],
76+
{
77+
host: replication_group,
78+
tags: tags
79+
}
80+
)
81+
raise "Error submitting metric for #{replication_group}" unless resp[0] == "202"
82+
@last_time_submitted = time
83+
resp
84+
end
85+
86+
def slowlog_time(slowlog)
87+
Time.at slowlog[1]
88+
end
89+
90+
def slowlog_microseconds(slowlog)
91+
slowlog[2]
92+
end
93+
94+
def client_ip(ip_and_port)
95+
ip_and_port.split(':')[0]
96+
end
97+
98+
def slowlog_tags(slowlog)
99+
{
100+
command: slowlog[3][0],
101+
client: client_ip(slowlog[4]),
102+
client_name: slowlog[5],
103+
replication_group: replication_group,
104+
service: replication_group,
105+
namespace: ENV.fetch('NAMESPACE'),
106+
aws: 'true',
107+
env: ENV.fetch('ENV')
108+
}
109+
end
110+
111+
def ship_slowlogs
112+
REDIS.slowlog('get').each do |slowlog|
113+
break if slowlog_time(slowlog) <= last_time_submitted
114+
emit_point(
115+
slowlog_time(slowlog),
116+
slowlog_microseconds(slowlog),
117+
slowlog_tags(slowlog)
118+
)
119+
end
120+
end
121+
122+
123+
def lambda_handler(event: {}, context: {})
124+
@event = event
125+
126+
log_context
127+
LOGGER.info "Event time: #{time}."
128+
begin
129+
REDIS.ping
130+
ship_slowlogs
131+
rescue StandardError => e
132+
LOGGER.error e.inspect
133+
# => #<Redis::CannotConnectError: Timed out connecting to Redis on 10.0.1.1:6380>
134+
LOGGER.error e.message
135+
# => Timed out connecting to Redis on 10.0.1.1:6380
136+
end
137+
138+
nil
139+
end
140+
141+
if __FILE__ == $0
142+
lambda_handler
143+
end

0 commit comments

Comments
 (0)