logidze 0.9.0 → 0.10.0
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.
- 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
|
|