piko-sharp-kit 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/piko-sharp-kit.gemspec +11 -0
  3. data/rack-mini-profiler-4.0.1/CHANGELOG.md +424 -0
  4. data/rack-mini-profiler-4.0.1/README.md +542 -0
  5. data/rack-mini-profiler-4.0.1/lib/enable_rails_patches.rb +5 -0
  6. data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/USAGE +9 -0
  7. data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/install_generator.rb +13 -0
  8. data/rack-mini-profiler-4.0.1/lib/generators/rack_mini_profiler/templates/rack_mini_profiler.rb +8 -0
  9. data/rack-mini-profiler-4.0.1/lib/generators/rack_profiler/USAGE +2 -0
  10. data/rack-mini-profiler-4.0.1/lib/generators/rack_profiler/install_generator.rb +16 -0
  11. data/rack-mini-profiler-4.0.1/lib/html/dot.1.1.2.min.js +2 -0
  12. data/rack-mini-profiler-4.0.1/lib/html/includes.css +474 -0
  13. data/rack-mini-profiler-4.0.1/lib/html/includes.js +1520 -0
  14. data/rack-mini-profiler-4.0.1/lib/html/includes.scss +698 -0
  15. data/rack-mini-profiler-4.0.1/lib/html/includes.tmpl +333 -0
  16. data/rack-mini-profiler-4.0.1/lib/html/pretty-print.js +810 -0
  17. data/rack-mini-profiler-4.0.1/lib/html/profile_handler.js +1 -0
  18. data/rack-mini-profiler-4.0.1/lib/html/rack-mini-profiler.css +3 -0
  19. data/rack-mini-profiler-4.0.1/lib/html/rack-mini-profiler.js +2 -0
  20. data/rack-mini-profiler-4.0.1/lib/html/share.html +10 -0
  21. data/rack-mini-profiler-4.0.1/lib/html/speedscope/LICENSE +21 -0
  22. data/rack-mini-profiler-4.0.1/lib/html/speedscope/README.md +3 -0
  23. data/rack-mini-profiler-4.0.1/lib/html/speedscope/demangle-cpp.1768f4cc.js +4 -0
  24. data/rack-mini-profiler-4.0.1/lib/html/speedscope/favicon-16x16.f74b3187.png +0 -0
  25. data/rack-mini-profiler-4.0.1/lib/html/speedscope/favicon-32x32.bc503437.png +0 -0
  26. data/rack-mini-profiler-4.0.1/lib/html/speedscope/file-format-schema.json +324 -0
  27. data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-regular.css +8 -0
  28. data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff +0 -0
  29. data/rack-mini-profiler-4.0.1/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff2 +0 -0
  30. data/rack-mini-profiler-4.0.1/lib/html/speedscope/import.8ae8aa3d.js +123 -0
  31. data/rack-mini-profiler-4.0.1/lib/html/speedscope/index.html +2 -0
  32. data/rack-mini-profiler-4.0.1/lib/html/speedscope/release.txt +3 -0
  33. data/rack-mini-profiler-4.0.1/lib/html/speedscope/reset.8c46b7a1.css +2 -0
  34. data/rack-mini-profiler-4.0.1/lib/html/speedscope/source-map.438fa06b.js +24 -0
  35. data/rack-mini-profiler-4.0.1/lib/html/speedscope/speedscope.f27db165.js +193 -0
  36. data/rack-mini-profiler-4.0.1/lib/html/vendor.js +848 -0
  37. data/rack-mini-profiler-4.0.1/lib/mini_profiler/actions.rb +157 -0
  38. data/rack-mini-profiler-4.0.1/lib/mini_profiler/asset_version.rb +6 -0
  39. data/rack-mini-profiler-4.0.1/lib/mini_profiler/client_settings.rb +127 -0
  40. data/rack-mini-profiler-4.0.1/lib/mini_profiler/config.rb +146 -0
  41. data/rack-mini-profiler-4.0.1/lib/mini_profiler/context.rb +14 -0
  42. data/rack-mini-profiler-4.0.1/lib/mini_profiler/gc_profiler.rb +158 -0
  43. data/rack-mini-profiler-4.0.1/lib/mini_profiler/profiling_methods.rb +167 -0
  44. data/rack-mini-profiler-4.0.1/lib/mini_profiler/snapshots_transporter.rb +111 -0
  45. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/abstract_store.rb +97 -0
  46. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/file_store.rb +179 -0
  47. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/memcache_store.rb +105 -0
  48. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/memory_store.rb +233 -0
  49. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage/redis_store.rb +353 -0
  50. data/rack-mini-profiler-4.0.1/lib/mini_profiler/storage.rb +7 -0
  51. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/base.rb +39 -0
  52. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/client.rb +90 -0
  53. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/custom.rb +25 -0
  54. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/page.rb +146 -0
  55. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/request.rb +186 -0
  56. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct/sql.rb +89 -0
  57. data/rack-mini-profiler-4.0.1/lib/mini_profiler/timer_struct.rb +8 -0
  58. data/rack-mini-profiler-4.0.1/lib/mini_profiler/version.rb +8 -0
  59. data/rack-mini-profiler-4.0.1/lib/mini_profiler/views.rb +198 -0
  60. data/rack-mini-profiler-4.0.1/lib/mini_profiler.rb +670 -0
  61. data/rack-mini-profiler-4.0.1/lib/mini_profiler_rails/railtie.rb +234 -0
  62. data/rack-mini-profiler-4.0.1/lib/mini_profiler_rails/railtie_methods.rb +61 -0
  63. data/rack-mini-profiler-4.0.1/lib/patches/db/activerecord.rb +44 -0
  64. data/rack-mini-profiler-4.0.1/lib/patches/db/mongo.rb +18 -0
  65. data/rack-mini-profiler-4.0.1/lib/patches/db/moped.rb +14 -0
  66. data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/alias_method.rb +30 -0
  67. data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/prepend.rb +34 -0
  68. data/rack-mini-profiler-4.0.1/lib/patches/db/mysql2.rb +7 -0
  69. data/rack-mini-profiler-4.0.1/lib/patches/db/neo4j.rb +16 -0
  70. data/rack-mini-profiler-4.0.1/lib/patches/db/nobrainer.rb +31 -0
  71. data/rack-mini-profiler-4.0.1/lib/patches/db/oracle_enhanced.rb +70 -0
  72. data/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb +121 -0
  73. data/rack-mini-profiler-4.0.1/lib/patches/db/pg/prepend.rb +115 -0
  74. data/rack-mini-profiler-4.0.1/lib/patches/db/pg.rb +7 -0
  75. data/rack-mini-profiler-4.0.1/lib/patches/db/plucky.rb +49 -0
  76. data/rack-mini-profiler-4.0.1/lib/patches/db/riak.rb +105 -0
  77. data/rack-mini-profiler-4.0.1/lib/patches/db/rsolr.rb +26 -0
  78. data/rack-mini-profiler-4.0.1/lib/patches/db/sequel.rb +12 -0
  79. data/rack-mini-profiler-4.0.1/lib/patches/net_patches.rb +27 -0
  80. data/rack-mini-profiler-4.0.1/lib/patches/sql_patches.rb +74 -0
  81. data/rack-mini-profiler-4.0.1/lib/prepend_mysql2_patch.rb +5 -0
  82. data/rack-mini-profiler-4.0.1/lib/prepend_net_http_patch.rb +5 -0
  83. data/rack-mini-profiler-4.0.1/lib/prepend_pg_patch.rb +5 -0
  84. data/rack-mini-profiler-4.0.1/lib/rack-mini-profiler.rb +13 -0
  85. data/rack-mini-profiler-4.0.1/rack-mini-profiler.gemspec +49 -0
  86. metadata +124 -0
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ class MiniProfiler
5
+ module ProfilingMethods
6
+
7
+ def record_sql(query, elapsed_ms, params = nil, cached = nil)
8
+ return unless current && current.current_timer
9
+ c = current
10
+ c.current_timer.add_sql(
11
+ redact_sql_queries? ? nil : query,
12
+ elapsed_ms,
13
+ c.page_struct,
14
+ redact_sql_queries? ? nil : params,
15
+ c.skip_backtrace,
16
+ c.full_backtrace,
17
+ cached
18
+ )
19
+ end
20
+
21
+ def report_reader_duration(elapsed_ms, row_count = nil, class_name = nil)
22
+ current&.current_timer&.report_reader_duration(elapsed_ms, row_count, class_name)
23
+ end
24
+
25
+ def start_step(name)
26
+ return unless current
27
+ parent_timer = current.current_timer
28
+ current.current_timer = current_timer = current.current_timer.add_child(name)
29
+ [current_timer, parent_timer]
30
+ end
31
+
32
+ def finish_step(obj)
33
+ return unless obj && current
34
+ current_timer, parent_timer = obj
35
+ current_timer.record_time
36
+ current.current_timer = parent_timer
37
+ end
38
+
39
+ # perform a profiling step on given block
40
+ def step(name, opts = nil)
41
+ if current
42
+ parent_timer = current.current_timer
43
+ current.current_timer = current_timer = current.current_timer.add_child(name)
44
+ begin
45
+ yield if block_given?
46
+ ensure
47
+ current_timer.record_time
48
+ current.current_timer = parent_timer
49
+ end
50
+ else
51
+ yield if block_given?
52
+ end
53
+ end
54
+
55
+ def unprofile_method(klass, method)
56
+
57
+ clean = clean_method_name(method)
58
+
59
+ with_profiling = ("#{clean}_with_mini_profiler").intern
60
+ without_profiling = ("#{clean}_without_mini_profiler").intern
61
+
62
+ if klass.send :method_defined?, with_profiling
63
+ klass.send :alias_method, method, without_profiling
64
+ klass.send :remove_method, with_profiling
65
+ klass.send :remove_method, without_profiling
66
+ end
67
+ end
68
+
69
+ def counter_method(klass, method, &blk)
70
+ self.profile_method(klass, method, :counter, &blk)
71
+ end
72
+
73
+ def uncounter_method(klass, method)
74
+ self.unprofile_method(klass, method)
75
+ end
76
+
77
+ def profile_method(klass, method, type = :profile, &blk)
78
+ default_name = type == :counter ? method.to_s : klass.to_s + " " + method.to_s
79
+ clean = clean_method_name(method)
80
+
81
+ with_profiling = ("#{clean}_with_mini_profiler").intern
82
+ without_profiling = ("#{clean}_without_mini_profiler").intern
83
+
84
+ if klass.send :method_defined?, with_profiling
85
+ return # dont double profile
86
+ end
87
+
88
+ klass.send :alias_method, without_profiling, method
89
+ klass.send :define_method, with_profiling do |*args, &orig|
90
+ return self.send without_profiling, *args, &orig unless Rack::MiniProfiler.current
91
+
92
+ name = default_name
93
+ if blk
94
+ name =
95
+ if respond_to?(:instance_exec)
96
+ instance_exec(*args, &blk)
97
+ else
98
+ # deprecated in Rails 4.x
99
+ blk.bind(self).call(*args)
100
+ end
101
+ end
102
+
103
+ parent_timer = Rack::MiniProfiler.current.current_timer
104
+
105
+ if type == :counter
106
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
107
+ begin
108
+ self.send without_profiling, *args, &orig
109
+ ensure
110
+ duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).to_f * 1000
111
+ parent_timer.add_custom(name, duration_ms, Rack::MiniProfiler.current.page_struct)
112
+ end
113
+ else
114
+ Rack::MiniProfiler.current.current_timer = current_timer = parent_timer.add_child(name)
115
+ begin
116
+ self.send without_profiling, *args, &orig
117
+ ensure
118
+ current_timer.record_time
119
+ Rack::MiniProfiler.current.current_timer = parent_timer
120
+ end
121
+ end
122
+ end
123
+ if klass.respond_to?(:ruby2_keywords, true)
124
+ klass.send(:ruby2_keywords, with_profiling)
125
+ end
126
+ klass.send :alias_method, method, with_profiling
127
+ end
128
+
129
+ def profile_singleton_method(klass, method, type = :profile, &blk)
130
+ profile_method(klass.singleton_class, method, type, &blk)
131
+ end
132
+
133
+ def unprofile_singleton_method(klass, method)
134
+ unprofile_method(klass.singleton_class, method)
135
+ end
136
+
137
+ # Add a custom timing. These are displayed similar to SQL/query time in
138
+ # columns expanding to the right.
139
+ #
140
+ # type - String counter type. Each distinct type gets its own column.
141
+ # duration_ms - Duration of the call in ms. Either this or a block must be
142
+ # given but not both.
143
+ #
144
+ # When a block is given, calculate the duration by yielding to the block
145
+ # and keeping a record of its run time.
146
+ #
147
+ # Returns the result of the block, or nil when no block is given.
148
+ def counter(type, duration_ms = nil)
149
+ result = nil
150
+ if block_given?
151
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
152
+ result = yield
153
+ duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).to_f * 1000
154
+ end
155
+ return result if current.nil? || !request_authorized?
156
+ current.current_timer.add_custom(type, duration_ms, current.page_struct)
157
+ result
158
+ end
159
+
160
+ private
161
+
162
+ def clean_method_name(method)
163
+ method.to_s.gsub(/[\?\!]/, "")
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ::Rack::MiniProfiler::SnapshotsTransporter
4
+ @@transported_snapshots_count = 0
5
+ @@successful_http_requests_count = 0
6
+ @@failed_http_requests_count = 0
7
+
8
+ class << self
9
+ def transported_snapshots_count
10
+ @@transported_snapshots_count
11
+ end
12
+
13
+ def successful_http_requests_count
14
+ @@successful_http_requests_count
15
+ end
16
+
17
+ def failed_http_requests_count
18
+ @@failed_http_requests_count
19
+ end
20
+
21
+ def transport(snapshot)
22
+ @transporter ||= self.new(Rack::MiniProfiler.config)
23
+ @transporter.ship(snapshot)
24
+ end
25
+ end
26
+
27
+ attr_reader :buffer
28
+ attr_accessor :max_buffer_size, :gzip_requests
29
+
30
+ def initialize(config)
31
+ @uri = URI(config.snapshots_transport_destination_url)
32
+ @auth_key = config.snapshots_transport_auth_key
33
+ @gzip_requests = config.snapshots_transport_gzip_requests
34
+ @thread = nil
35
+ @thread_mutex = Mutex.new
36
+ @buffer = []
37
+ @buffer_mutex = Mutex.new
38
+ @max_buffer_size = 100
39
+ @consecutive_failures_count = 0
40
+ @testing = false
41
+ end
42
+
43
+ def ship(snapshot)
44
+ @buffer_mutex.synchronize do
45
+ @buffer << snapshot
46
+ @buffer.shift if @buffer.size > @max_buffer_size
47
+ end
48
+ @thread_mutex.synchronize { start_thread }
49
+ end
50
+
51
+ def flush_buffer
52
+ buffer_content = @buffer_mutex.synchronize do
53
+ @buffer.dup if @buffer.size > 0
54
+ end
55
+ if buffer_content
56
+ headers = {
57
+ 'Content-Type' => 'application/json',
58
+ 'Mini-Profiler-Transport-Auth' => @auth_key
59
+ }
60
+ json = { snapshots: buffer_content }.to_json
61
+ body = if @gzip_requests
62
+ require 'zlib'
63
+ io = StringIO.new
64
+ gzip_writer = Zlib::GzipWriter.new(io)
65
+ gzip_writer.write(json)
66
+ gzip_writer.close
67
+ headers['Content-Encoding'] = 'gzip'
68
+ io.string
69
+ else
70
+ json
71
+ end
72
+ request = Net::HTTP::Post.new(@uri, headers)
73
+ request.body = body
74
+ http = Net::HTTP.new(@uri.hostname, @uri.port)
75
+ http.use_ssl = @uri.scheme == 'https'
76
+ res = http.request(request)
77
+ if res.code.to_i == 200
78
+ @@successful_http_requests_count += 1
79
+ @@transported_snapshots_count += buffer_content.size
80
+ @buffer_mutex.synchronize do
81
+ @buffer -= buffer_content
82
+ end
83
+ @consecutive_failures_count = 0
84
+ else
85
+ @@failed_http_requests_count += 1
86
+ @consecutive_failures_count += 1
87
+ end
88
+ end
89
+ end
90
+
91
+ def requests_interval
92
+ [30 + backoff_delay, 60 * 60].min
93
+ end
94
+
95
+ private
96
+
97
+ def backoff_delay
98
+ return 0 if @consecutive_failures_count == 0
99
+ 2**@consecutive_failures_count
100
+ end
101
+
102
+ def start_thread
103
+ return if @thread&.alive? || @testing
104
+ @thread = Thread.new do
105
+ while true
106
+ sleep requests_interval
107
+ flush_buffer
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ class MiniProfiler
5
+ class AbstractStore
6
+
7
+ # maximum age of allowed tokens before cycling in seconds
8
+ MAX_TOKEN_AGE = 1800
9
+
10
+ def save(page_struct)
11
+ raise NotImplementedError.new("save is not implemented")
12
+ end
13
+
14
+ def load(id)
15
+ raise NotImplementedError.new("load is not implemented")
16
+ end
17
+
18
+ def set_unviewed(user, id)
19
+ raise NotImplementedError.new("set_unviewed is not implemented")
20
+ end
21
+
22
+ def set_viewed(user, id)
23
+ raise NotImplementedError.new("set_viewed is not implemented")
24
+ end
25
+
26
+ def set_all_unviewed(user, ids)
27
+ raise NotImplementedError.new("set_all_unviewed is not implemented")
28
+ end
29
+
30
+ def get_unviewed_ids(user)
31
+ raise NotImplementedError.new("get_unviewed_ids is not implemented")
32
+ end
33
+
34
+ def diagnostics(user)
35
+ # this is opt in, no need to explode if not implemented
36
+ ""
37
+ end
38
+
39
+ # a list of tokens that are permitted to access profiler in explicit mode
40
+ def allowed_tokens
41
+ raise NotImplementedError.new("allowed_tokens is not implemented")
42
+ end
43
+
44
+ def should_take_snapshot?(period)
45
+ raise NotImplementedError.new("should_take_snapshot? is not implemented")
46
+ end
47
+
48
+ def push_snapshot(page_struct, group_name, config)
49
+ raise NotImplementedError.new("push_snapshot is not implemented")
50
+ end
51
+
52
+ # returns a hash where the keys are group names and the values
53
+ # are hashes that contain 3 keys:
54
+ # 1. `:worst_score` => the duration of the worst/slowest snapshot in the group (float)
55
+ # 2. `:best_score` => the duration of the best/fastest snapshot in the group (float)
56
+ # 3. `:snapshots_count` => the number of snapshots in the group (integer)
57
+ def fetch_snapshots_overview
58
+ raise NotImplementedError.new("fetch_snapshots_overview is not implemented")
59
+ end
60
+
61
+ # @param group_name [String]
62
+ # @return [Array<Rack::MiniProfiler::TimerStruct::Page>] list of snapshots of the group. Blank array if the group doesn't exist.
63
+ def fetch_snapshots_group(group_name)
64
+ raise NotImplementedError.new("fetch_snapshots_group is not implemented")
65
+ end
66
+
67
+ def load_snapshot(id, group_name)
68
+ raise NotImplementedError.new("load_snapshot is not implemented")
69
+ end
70
+
71
+ def snapshots_overview
72
+ groups = fetch_snapshots_overview.to_a
73
+ groups.sort_by! { |name, hash| hash[:worst_score] }
74
+ groups.reverse!
75
+ groups.map! { |name, hash| hash.merge(name: name) }
76
+ groups
77
+ end
78
+
79
+ def snapshots_group(group_name)
80
+ snapshots = fetch_snapshots_group(group_name)
81
+ data = []
82
+ snapshots.each do |snapshot|
83
+ data << {
84
+ id: snapshot[:id],
85
+ duration: snapshot.duration_ms,
86
+ sql_count: snapshot[:sql_count],
87
+ timestamp: snapshot[:started_at],
88
+ custom_fields: snapshot[:custom_fields]
89
+ }
90
+ end
91
+ data.sort_by! { |s| s[:duration] }
92
+ data.reverse!
93
+ data
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Rack
6
+ class MiniProfiler
7
+ class FileStore < AbstractStore
8
+
9
+ # Sub-class thread so we have a named thread (useful for debugging in Thread.list).
10
+ class CacheCleanupThread < Thread
11
+ end
12
+
13
+ class FileCache
14
+ def initialize(path, prefix)
15
+ @path = path
16
+ @prefix = prefix
17
+ end
18
+
19
+ def [](key)
20
+ begin
21
+ data = ::File.open(path(key), "rb") { |f| f.read }
22
+ # rubocop:disable Security/MarshalLoad
23
+ Marshal.load data
24
+ # rubocop:enable Security/MarshalLoad
25
+ rescue
26
+ nil
27
+ end
28
+ end
29
+
30
+ def []=(key, val)
31
+ ::File.open(path(key), "wb+") do |f|
32
+ f.sync = true
33
+ f.write Marshal.dump(val)
34
+ end
35
+ end
36
+
37
+ private
38
+ if Gem.win_platform?
39
+ def path(key)
40
+ @path.dup << "/" << @prefix << "_" << key.gsub(/:/, '_')
41
+ end
42
+ else
43
+ def path(key)
44
+ @path.dup << "/" << @prefix << "_" << key
45
+ end
46
+ end
47
+ end
48
+
49
+ EXPIRES_IN_SECONDS = 60 * 60 * 24
50
+
51
+ def initialize(args = nil)
52
+ args ||= {}
53
+ @path = args[:path]
54
+ @expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
55
+ raise ArgumentError.new :path unless @path
56
+ FileUtils.mkdir_p(@path) unless ::File.exist?(@path)
57
+
58
+ @timer_struct_cache = FileCache.new(@path, "mp_timers")
59
+ @timer_struct_lock = Mutex.new
60
+ @user_view_cache = FileCache.new(@path, "mp_views")
61
+ @user_view_lock = Mutex.new
62
+
63
+ @auth_token_cache = FileCache.new(@path, "tokens")
64
+ @auth_token_lock = Mutex.new
65
+
66
+ me = self
67
+ t = CacheCleanupThread.new do
68
+ interval = 10
69
+ cleanup_cache_cycle = 3600
70
+ cycle_count = 1
71
+
72
+ begin
73
+ until Thread.current[:should_exit] do
74
+ # TODO: a sane retry count before bailing
75
+
76
+ # We don't want to hit the filesystem every 10s to clean up the cache so we need to do a bit of
77
+ # accounting to avoid sleeping that entire time. We don't want to sleep for the entire period because
78
+ # it means the thread will stay live in hot deployment scenarios, keeping a potentially large memory
79
+ # graph from being garbage collected upon undeploy.
80
+ if cycle_count * interval >= cleanup_cache_cycle
81
+ cycle_count = 1
82
+ me.cleanup_cache
83
+ end
84
+
85
+ sleep(interval)
86
+ cycle_count += 1
87
+ end
88
+ rescue
89
+ # don't crash the thread, we can clean up next time
90
+ end
91
+ end
92
+
93
+ at_exit { t[:should_exit] = true }
94
+ end
95
+
96
+ def save(page_struct)
97
+ @timer_struct_lock.synchronize {
98
+ @timer_struct_cache[page_struct[:id]] = page_struct
99
+ }
100
+ end
101
+
102
+ def load(id)
103
+ @timer_struct_lock.synchronize {
104
+ @timer_struct_cache[id]
105
+ }
106
+ end
107
+
108
+ def set_unviewed(user, id)
109
+ @user_view_lock.synchronize {
110
+ current = @user_view_cache[user]
111
+ current = [] unless Array === current
112
+ current << id
113
+ @user_view_cache[user] = current.uniq
114
+ }
115
+ end
116
+
117
+ def set_viewed(user, id)
118
+ @user_view_lock.synchronize {
119
+ @user_view_cache[user] ||= []
120
+ current = @user_view_cache[user]
121
+ current = [] unless Array === current
122
+ current.delete(id)
123
+ @user_view_cache[user] = current.uniq
124
+ }
125
+ end
126
+
127
+ def set_all_unviewed(user, ids)
128
+ @user_view_lock.synchronize {
129
+ @user_view_cache[user] = ids.uniq
130
+ }
131
+ end
132
+
133
+ def get_unviewed_ids(user)
134
+ @user_view_lock.synchronize {
135
+ @user_view_cache[user]
136
+ }
137
+ end
138
+
139
+ def flush_tokens
140
+ @auth_token_lock.synchronize {
141
+ @auth_token_cache[""] = nil
142
+ }
143
+ end
144
+
145
+ def allowed_tokens
146
+ @auth_token_lock.synchronize {
147
+ token1, token2, cycle_at = @auth_token_cache[""]
148
+
149
+ unless cycle_at && (Float === cycle_at) && (cycle_at > Process.clock_gettime(Process::CLOCK_MONOTONIC))
150
+ token2 = token1
151
+ token1 = SecureRandom.hex
152
+ cycle_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + Rack::MiniProfiler::AbstractStore::MAX_TOKEN_AGE
153
+ end
154
+
155
+ @auth_token_cache[""] = [token1, token2, cycle_at]
156
+
157
+ [token1, token2].compact
158
+ }
159
+ end
160
+
161
+ def cleanup_cache
162
+ files = Dir.entries(@path)
163
+ @timer_struct_lock.synchronize {
164
+ files.each do |f|
165
+ f = @path + '/' + f
166
+ ::File.delete f if ::File.basename(f) =~ (/^mp_timers/) && ((Time.now - ::File.mtime(f)) > @expires_in_seconds)
167
+ end
168
+ }
169
+ @user_view_lock.synchronize {
170
+ files.each do |f|
171
+ f = @path + '/' + f
172
+ ::File.delete f if ::File.basename(f) =~ (/^mp_views/) && ((Time.now - ::File.mtime(f)) > @expires_in_seconds)
173
+ end
174
+ }
175
+ end
176
+
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ class MiniProfiler
5
+ class MemcacheStore < AbstractStore
6
+
7
+ EXPIRES_IN_SECONDS = 60 * 60 * 24
8
+ MAX_RETRIES = 10
9
+
10
+ def initialize(args = nil)
11
+ require 'dalli' unless defined? Dalli
12
+ args ||= {}
13
+
14
+ @prefix = args[:prefix] || "MPMemcacheStore"
15
+ @prefix += "-#{Rack::MiniProfiler::VERSION}"
16
+
17
+ @client = args[:client] || Dalli::Client.new
18
+ @expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
19
+ end
20
+
21
+ def alive?
22
+ begin
23
+ @client.alive!
24
+ true
25
+ rescue Dalli::RingError
26
+ false
27
+ end
28
+ end
29
+
30
+ def save(page_struct)
31
+ @client.set("#{@prefix}#{page_struct[:id]}", Marshal::dump(page_struct), @expires_in_seconds)
32
+ end
33
+
34
+ def load(id)
35
+ raw = @client.get("#{@prefix}#{id}")
36
+ # rubocop:disable Security/MarshalLoad
37
+ Marshal.load(raw) if raw
38
+ # rubocop:enable Security/MarshalLoad
39
+ end
40
+
41
+ def set_unviewed(user, id)
42
+ @client.add("#{@prefix}-#{user}-v", [], @expires_in_seconds)
43
+ MAX_RETRIES.times do
44
+ break if @client.cas("#{@prefix}-#{user}-v", @expires_in_seconds) do |ids|
45
+ ids << id if !ids.include?(id)
46
+ ids
47
+ end
48
+ end
49
+ end
50
+
51
+ def set_viewed(user, id)
52
+ @client.add("#{@prefix}-#{user}-v", [], @expires_in_seconds)
53
+ MAX_RETRIES.times do
54
+ break if @client.cas("#{@prefix}-#{user}-v", @expires_in_seconds) do |ids|
55
+ ids.delete id
56
+ ids
57
+ end
58
+ end
59
+ end
60
+
61
+ def set_all_unviewed(user, ids)
62
+ @client.set("#{@prefix}-#{user}-v", ids, @expires_in_seconds)
63
+ end
64
+
65
+ def get_unviewed_ids(user)
66
+ @client.get("#{@prefix}-#{user}-v") || []
67
+ end
68
+
69
+ def flush_tokens
70
+ @client.set("#{@prefix}-tokens", nil)
71
+ end
72
+
73
+ def allowed_tokens
74
+
75
+ token_info = @client.get("#{@prefix}-tokens")
76
+ key1, key2, cycle_at = nil
77
+
78
+ if token_info
79
+ # rubocop:disable Security/MarshalLoad
80
+ key1, key2, cycle_at = Marshal.load(token_info)
81
+ # rubocop:enable Security/MarshalLoad
82
+
83
+ key1 = nil unless key1 && key1.length == 32
84
+ key2 = nil unless key2 && key2.length == 32
85
+
86
+ if key1 && cycle_at && (cycle_at > Process.clock_gettime(Process::CLOCK_MONOTONIC))
87
+ return [key1, key2].compact
88
+ end
89
+ end
90
+
91
+ timeout = Rack::MiniProfiler::AbstractStore::MAX_TOKEN_AGE
92
+
93
+ # cycle keys
94
+ key2 = key1
95
+ key1 = SecureRandom.hex
96
+ cycle_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
97
+
98
+ @client.set("#{@prefix}-tokens", Marshal::dump([key1, key2, cycle_at]))
99
+
100
+ [key1, key2].compact
101
+ end
102
+
103
+ end
104
+ end
105
+ end