logidze 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitattributes +3 -0
- data/.github/ISSUE_TEMPLATE.md +20 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +29 -0
- data/.rubocop.yml +30 -69
- data/.travis.yml +8 -5
- data/CHANGELOG.md +35 -1
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +67 -26
- data/Rakefile +2 -2
- data/assets/pg_log_data_chart.png +0 -0
- data/bench/performance/README.md +2 -2
- data/bench/performance/diff_bench.rb +15 -13
- data/bench/performance/insert_bench.rb +6 -4
- data/bench/performance/memory_profile.rb +12 -9
- data/bench/performance/setup.rb +35 -28
- data/bench/performance/update_bench.rb +8 -6
- data/bin/setup +2 -2
- data/gemfiles/rails42.gemfile +1 -0
- data/gemfiles/rails6.gemfile +6 -0
- data/lib/generators/logidze/install/install_generator.rb +3 -2
- data/lib/generators/logidze/model/model_generator.rb +12 -12
- data/lib/logidze.rb +44 -18
- data/lib/logidze/engine.rb +2 -1
- data/lib/logidze/has_logidze.rb +14 -4
- data/lib/logidze/history.rb +6 -5
- data/lib/logidze/history/type.rb +1 -1
- data/lib/logidze/history/version.rb +6 -5
- data/lib/logidze/ignore_log_data.rb +31 -4
- data/lib/logidze/ignore_log_data/association.rb +11 -0
- data/lib/logidze/ignore_log_data/default_scope_patch.rb +25 -0
- data/lib/logidze/ignore_log_data/ignored_columns.rb +1 -1
- data/lib/logidze/ignore_log_data/missing_attribute_patch.rb +3 -1
- data/lib/logidze/meta.rb +1 -1
- data/lib/logidze/migration.rb +1 -0
- data/lib/logidze/model.rb +14 -4
- data/lib/logidze/version.rb +2 -1
- data/lib/logidze/versioned_association.rb +0 -1
- data/logidze.gemspec +16 -8
- metadata +28 -60
- data/.hound.yml +0 -3
data/Rakefile
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
3
|
require "rspec/core/rake_task"
|
4
4
|
require "rubocop/rake_task"
|
5
5
|
|
@@ -25,4 +25,4 @@ RSpec::Core::RakeTask.new("spec:acceptance") do |task|
|
|
25
25
|
end
|
26
26
|
|
27
27
|
desc "Run the specs and acceptance tests"
|
28
|
-
task default: %w
|
28
|
+
task default: %w[rubocop spec spec:acceptance]
|
Binary file
|
data/bench/performance/README.md
CHANGED
@@ -17,14 +17,14 @@ When changeset has 2 fields:
|
|
17
17
|
|
18
18
|
```
|
19
19
|
PaperTrail UPDATE #1 256.651 (±26.5%) i/s - 1.206k in 5.002300s
|
20
|
-
|
20
|
+
Logidze UPDATE #1 356.932 (±12.6%) i/s - 1.764k in 5.030560s
|
21
21
|
```
|
22
22
|
|
23
23
|
When changeset has 5 fields:
|
24
24
|
|
25
25
|
```
|
26
26
|
PaperTrail UPDATE #2 246.281 (±24.0%) i/s - 1.168k in 5.008234s
|
27
|
-
|
27
|
+
Logidze UPDATE #2 331.942 (±16.6%) i/s - 1.593k in 5.028135s
|
28
28
|
```
|
29
29
|
|
30
30
|
## Getting diff ([source](diff_bench.rb))
|
@@ -1,36 +1,38 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "benchmark/ips"
|
4
|
+
require "./setup"
|
3
5
|
|
4
6
|
# How many records do you want?
|
5
|
-
N = (ENV[
|
7
|
+
N = (ENV["N"] || "100").to_i
|
6
8
|
|
7
9
|
# How many version each record has?
|
8
|
-
V = (ENV[
|
10
|
+
V = (ENV["V"] || "10").to_i
|
9
11
|
|
10
12
|
# Benchmark run time
|
11
|
-
BM_TIME = (ENV[
|
13
|
+
BM_TIME = (ENV["BM_TIME"] || 5).to_i
|
12
14
|
|
13
15
|
BM_WARMUP = [(BM_TIME / 10), 2].max
|
14
16
|
|
15
17
|
LogidzeBench.cleanup
|
16
18
|
LogidzeBench.populate(N)
|
17
19
|
|
18
|
-
ts1 = LogidzeBench.generate_versions(V/2)
|
20
|
+
ts1 = LogidzeBench.generate_versions(V / 2)
|
19
21
|
|
20
|
-
LogidzeBench.generate_versions(V/2)
|
22
|
+
LogidzeBench.generate_versions(V / 2)
|
21
23
|
|
22
24
|
Benchmark.ips do |x|
|
23
25
|
x.config(time: BM_TIME, warmup: BM_WARMUP)
|
24
26
|
|
25
|
-
x.report(
|
26
|
-
User.random(N/2).diff_from(ts1)
|
27
|
+
x.report("PT DIFF") do
|
28
|
+
User.random(N / 2).diff_from(ts1)
|
27
29
|
end
|
28
30
|
|
29
|
-
x.report(
|
30
|
-
User.random(N/2).diff_from_joined(ts1)
|
31
|
+
x.report("PT (join) DIFF") do
|
32
|
+
User.random(N / 2).diff_from_joined(ts1)
|
31
33
|
end
|
32
34
|
|
33
|
-
x.report(
|
34
|
-
LogidzeUser.random(N/2).diff_from(ts1)
|
35
|
+
x.report("Logidze DIFF") do
|
36
|
+
LogidzeUser.random(N / 2).diff_from(ts1)
|
35
37
|
end
|
36
38
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "benchmark/ips"
|
4
|
+
require "./setup"
|
3
5
|
|
4
6
|
params = {
|
5
7
|
email: Faker::Internet.email,
|
@@ -10,11 +12,11 @@ params = {
|
|
10
12
|
}
|
11
13
|
|
12
14
|
Benchmark.ips do |x|
|
13
|
-
x.report(
|
15
|
+
x.report("PaperTrail INSERT") do
|
14
16
|
User.create!(params)
|
15
17
|
end
|
16
18
|
|
17
|
-
x.report(
|
19
|
+
x.report("Logidze INSERT") do
|
18
20
|
LogidzeUser.create!(params)
|
19
21
|
end
|
20
22
|
end
|
@@ -1,12 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./setup"
|
4
|
+
require "active_support/core_ext"
|
5
|
+
require "memory_profiler"
|
4
6
|
|
5
7
|
# How many records do you want?
|
6
|
-
N = (ENV[
|
8
|
+
N = (ENV["N"] || "10").to_i
|
7
9
|
|
8
10
|
# How many version each record has?
|
9
|
-
V = (ENV[
|
11
|
+
V = (ENV["V"] || "10").to_i
|
10
12
|
|
11
13
|
LogidzeBench.cleanup
|
12
14
|
LogidzeBench.populate(N)
|
@@ -17,6 +19,7 @@ module MemoryReport
|
|
17
19
|
MEGA_BYTE = 1024 * 1024
|
18
20
|
|
19
21
|
module_function
|
22
|
+
|
20
23
|
def call(msg, relation)
|
21
24
|
buffer = nil
|
22
25
|
delta = N / 10
|
@@ -36,6 +39,7 @@ module MemoryReport
|
|
36
39
|
end
|
37
40
|
|
38
41
|
module_function
|
42
|
+
|
39
43
|
def to_human_size(size)
|
40
44
|
if size > MEGA_BYTE
|
41
45
|
"#{(size.to_f / MEGA_BYTE).round(2)} MB"
|
@@ -47,7 +51,6 @@ module MemoryReport
|
|
47
51
|
end
|
48
52
|
end
|
49
53
|
|
50
|
-
|
51
|
-
MemoryReport.("PT
|
52
|
-
MemoryReport.("
|
53
|
-
MemoryReport.("Logidze records", LogidzeUser.all)
|
54
|
+
MemoryReport.call("PT records", User.all)
|
55
|
+
MemoryReport.call("PT with versions", User.joins(:versions).all)
|
56
|
+
MemoryReport.call("Logidze records", LogidzeUser.all)
|
data/bench/performance/setup.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
begin
|
2
|
-
require
|
4
|
+
require "bundler/inline"
|
3
5
|
rescue LoadError => e
|
4
|
-
|
6
|
+
warn "Bundler version 1.10 or later is required. Please update your Bundler"
|
5
7
|
raise e
|
6
8
|
end
|
7
9
|
|
8
10
|
gemfile(true) do
|
9
|
-
source
|
10
|
-
gem
|
11
|
-
gem
|
12
|
-
gem
|
13
|
-
gem
|
14
|
-
gem
|
15
|
-
gem
|
16
|
-
gem
|
11
|
+
source "https://rubygems.org"
|
12
|
+
gem "activerecord", "~>4.2"
|
13
|
+
gem "pg"
|
14
|
+
gem "paper_trail", "~>4.2", require: false
|
15
|
+
gem "pry-byebug"
|
16
|
+
gem "faker"
|
17
|
+
gem "benchmark-ips"
|
18
|
+
gem "memory_profiler"
|
17
19
|
end
|
18
20
|
|
19
|
-
DB_NAME = ENV[
|
21
|
+
DB_NAME = ENV["DB_NAME"] || "logidze_query_bench"
|
20
22
|
|
21
23
|
begin
|
22
24
|
system("createdb #{DB_NAME}")
|
@@ -24,24 +26,25 @@ rescue
|
|
24
26
|
$stdout.puts "DB already exists"
|
25
27
|
end
|
26
28
|
|
27
|
-
$LOAD_PATH.unshift File.expand_path(
|
29
|
+
$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__)
|
28
30
|
|
29
|
-
require
|
30
|
-
require
|
31
|
-
require
|
31
|
+
require "active_record"
|
32
|
+
require "logger"
|
33
|
+
require "logidze"
|
32
34
|
|
33
35
|
ActiveRecord::Base.send :include, Logidze::HasLogidze
|
34
36
|
|
35
|
-
ActiveRecord::Base.establish_connection(adapter:
|
37
|
+
ActiveRecord::Base.establish_connection(adapter: "postgresql", database: DB_NAME)
|
36
38
|
|
37
39
|
at_exit do
|
38
40
|
ActiveRecord::Base.connection.disconnect!
|
39
41
|
end
|
40
42
|
|
41
|
-
require
|
43
|
+
require "paper_trail"
|
42
44
|
|
43
45
|
module LogidzeBench
|
44
46
|
module_function
|
47
|
+
|
45
48
|
def setup_db
|
46
49
|
ActiveRecord::Schema.define do
|
47
50
|
# PaperTrail setup
|
@@ -171,7 +174,7 @@ module LogidzeBench
|
|
171
174
|
jsonb_set(
|
172
175
|
NEW.log_data->'h',
|
173
176
|
'{1}',
|
174
|
-
merged
|
177
|
+
merged
|
175
178
|
) - 0
|
176
179
|
);
|
177
180
|
END IF;
|
@@ -198,7 +201,7 @@ module LogidzeBench
|
|
198
201
|
t.string :name
|
199
202
|
t.text :bio
|
200
203
|
t.integer :age
|
201
|
-
t.jsonb :log_data, default:
|
204
|
+
t.jsonb :log_data, default: "{}", null: false
|
202
205
|
t.timestamps
|
203
206
|
end
|
204
207
|
|
@@ -212,6 +215,7 @@ module LogidzeBench
|
|
212
215
|
end
|
213
216
|
|
214
217
|
module_function
|
218
|
+
|
215
219
|
def populate(n = 1_000)
|
216
220
|
n.times do
|
217
221
|
params = fake_params
|
@@ -221,6 +225,7 @@ module LogidzeBench
|
|
221
225
|
end
|
222
226
|
|
223
227
|
module_function
|
228
|
+
|
224
229
|
def cleanup
|
225
230
|
LogidzeUser.delete_all
|
226
231
|
User.delete_all
|
@@ -228,8 +233,9 @@ module LogidzeBench
|
|
228
233
|
end
|
229
234
|
|
230
235
|
module_function
|
236
|
+
|
231
237
|
def generate_versions(num = 1)
|
232
|
-
num.times do
|
238
|
+
num.times do
|
233
239
|
User.find_each do |u|
|
234
240
|
u.update!(fake_params(sample: true))
|
235
241
|
end
|
@@ -245,6 +251,7 @@ module LogidzeBench
|
|
245
251
|
end
|
246
252
|
|
247
253
|
module_function
|
254
|
+
|
248
255
|
def fake_params(sample: false)
|
249
256
|
params = {
|
250
257
|
email: Faker::Internet.email,
|
@@ -254,14 +261,14 @@ module LogidzeBench
|
|
254
261
|
bio: Faker::Lorem.paragraph
|
255
262
|
}
|
256
263
|
|
257
|
-
return params.slice(%i
|
264
|
+
return params.slice(%i[email position name age bio].sample) if sample
|
258
265
|
params
|
259
266
|
end
|
260
267
|
end
|
261
268
|
|
262
269
|
module ARandom
|
263
270
|
def random(num = 1)
|
264
|
-
rel = order(
|
271
|
+
rel = order("random()")
|
265
272
|
num == 1 ? rel.first : rel.limit(num)
|
266
273
|
end
|
267
274
|
end
|
@@ -271,11 +278,11 @@ class User < ActiveRecord::Base
|
|
271
278
|
has_paper_trail
|
272
279
|
|
273
280
|
def self.diff_from(ts)
|
274
|
-
includes(:versions).map { |u| {
|
281
|
+
includes(:versions).map { |u| {"id" => u.id, "changes" => u.diff_from(ts)} }
|
275
282
|
end
|
276
283
|
|
277
284
|
def self.diff_from_joined(ts)
|
278
|
-
eager_load(:versions).map { |u| {
|
285
|
+
eager_load(:versions).map { |u| {"id" => u.id, "changes" => u.diff_from(ts)} }
|
279
286
|
end
|
280
287
|
|
281
288
|
def diff_from(ts)
|
@@ -290,11 +297,11 @@ class User < ActiveRecord::Base
|
|
290
297
|
private
|
291
298
|
|
292
299
|
def merge_changeset(acc, data)
|
293
|
-
data.each do |k,v|
|
300
|
+
data.each do |k, v|
|
294
301
|
unless acc.key?(k)
|
295
|
-
acc[k] = {
|
302
|
+
acc[k] = {"old" => v[0]}
|
296
303
|
end
|
297
|
-
acc[k][
|
304
|
+
acc[k]["new"] = v[1]
|
298
305
|
end
|
299
306
|
end
|
300
307
|
end
|
@@ -305,4 +312,4 @@ class LogidzeUser < ActiveRecord::Base
|
|
305
312
|
end
|
306
313
|
|
307
314
|
# Run migration only if neccessary
|
308
|
-
LogidzeBench.setup_db if ENV[
|
315
|
+
LogidzeBench.setup_db if ENV["FORCE"].present? || !ActiveRecord::Base.connection.tables.include?("logidze_users")
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "benchmark/ips"
|
4
|
+
require "./setup"
|
3
5
|
|
4
6
|
params = {
|
5
7
|
age: Faker::Number.number(2),
|
@@ -18,19 +20,19 @@ LogidzeBench.cleanup
|
|
18
20
|
LogidzeBench.populate
|
19
21
|
|
20
22
|
Benchmark.ips do |x|
|
21
|
-
x.report(
|
23
|
+
x.report("PT UPDATE #1") do
|
22
24
|
User.random.update!(params)
|
23
25
|
end
|
24
26
|
|
25
|
-
x.report(
|
27
|
+
x.report("Logidze UPDATE #1") do
|
26
28
|
LogidzeUser.random.update!(params)
|
27
29
|
end
|
28
30
|
|
29
|
-
x.report(
|
31
|
+
x.report("PT UPDATE #2") do
|
30
32
|
User.random.update!(params2)
|
31
33
|
end
|
32
34
|
|
33
|
-
x.report(
|
35
|
+
x.report("Logidze UPDATE #2") do
|
34
36
|
LogidzeUser.random.update!(params2)
|
35
37
|
end
|
36
38
|
end
|
data/bin/setup
CHANGED
data/gemfiles/rails42.gemfile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "rails/generators"
|
3
4
|
require "rails/generators/active_record"
|
4
5
|
|
@@ -7,7 +8,7 @@ module Logidze
|
|
7
8
|
class InstallGenerator < ::Rails::Generators::Base # :nodoc:
|
8
9
|
include Rails::Generators::Migration
|
9
10
|
|
10
|
-
source_root File.expand_path(
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
11
12
|
|
12
13
|
class_option :update, type: :boolean, optional: true,
|
13
14
|
desc: "Define whether this is an update migration"
|
@@ -25,7 +26,7 @@ module Logidze
|
|
25
26
|
no_tasks do
|
26
27
|
def migration_name
|
27
28
|
if update?
|
28
|
-
"logidze_update_#{Logidze::VERSION.delete(
|
29
|
+
"logidze_update_#{Logidze::VERSION.delete(".")}"
|
29
30
|
else
|
30
31
|
"logidze_install"
|
31
32
|
end
|
@@ -1,12 +1,12 @@
|
|
1
|
-
# rubocop:disable Metrics/BlockLength
|
2
1
|
# frozen_string_literal: true
|
2
|
+
|
3
3
|
require "rails/generators"
|
4
4
|
require "rails/generators/active_record/migration/migration_generator"
|
5
5
|
|
6
6
|
module Logidze
|
7
7
|
module Generators
|
8
8
|
class ModelGenerator < ::ActiveRecord::Generators::Base # :nodoc:
|
9
|
-
source_root File.expand_path(
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
10
10
|
|
11
11
|
class_option :limit, type: :numeric, optional: true, desc: "Specify history size limit"
|
12
12
|
|
@@ -77,17 +77,17 @@ module Logidze
|
|
77
77
|
|
78
78
|
def columns_blacklist
|
79
79
|
array = if !options[:whitelist]
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
80
|
+
options[:blacklist]
|
81
|
+
else
|
82
|
+
class_name.constantize.column_names - options[:whitelist]
|
83
|
+
end
|
84
84
|
|
85
85
|
format_pgsql_array(array)
|
86
86
|
end
|
87
87
|
|
88
88
|
def timestamp_column
|
89
|
-
value = options[:timestamp_column] ||
|
90
|
-
return if %w
|
89
|
+
value = options[:timestamp_column] || "updated_at"
|
90
|
+
return if %w[nil null false].include?(value)
|
91
91
|
|
92
92
|
escape_pgsql_string(value)
|
93
93
|
end
|
@@ -101,13 +101,13 @@ module Logidze
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def logidze_snapshot_parameters
|
104
|
-
format_pgsql_args(
|
104
|
+
format_pgsql_args("to_jsonb(t)", timestamp_column, columns_blacklist)
|
105
105
|
end
|
106
106
|
|
107
107
|
def format_pgsql_array(ruby_array)
|
108
108
|
return if ruby_array.blank?
|
109
109
|
|
110
|
-
"'{" + ruby_array.join(
|
110
|
+
"'{" + ruby_array.join(", ") + "}'"
|
111
111
|
end
|
112
112
|
|
113
113
|
def escape_pgsql_string(string)
|
@@ -124,10 +124,10 @@ module Logidze
|
|
124
124
|
def format_pgsql_args(*values)
|
125
125
|
args = []
|
126
126
|
values.reverse_each do |value|
|
127
|
-
formatted_value = value.presence || (args.any? &&
|
127
|
+
formatted_value = value.presence || (args.any? && "null")
|
128
128
|
args << formatted_value if formatted_value
|
129
129
|
end
|
130
|
-
args.compact.reverse.join(
|
130
|
+
args.compact.reverse.join(", ")
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|