logidze 0.12.0 → 1.0.0.rc1
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/CHANGELOG.md +29 -5
- data/LICENSE.txt +1 -1
- data/README.md +263 -103
- data/lib/generators/logidze/fx_helper.rb +17 -0
- data/lib/generators/logidze/inject_sql.rb +18 -0
- data/lib/generators/logidze/install/USAGE +6 -1
- data/lib/generators/logidze/install/functions/logidze_compact_history.sql +38 -0
- data/lib/generators/logidze/install/functions/logidze_filter_keys.sql +27 -0
- data/lib/generators/logidze/install/functions/logidze_logger.sql +150 -0
- data/lib/generators/logidze/install/functions/logidze_snapshot.sql +24 -0
- data/lib/generators/logidze/install/functions/logidze_version.sql +20 -0
- data/lib/generators/logidze/install/install_generator.rb +58 -1
- data/lib/generators/logidze/install/templates/hstore.rb.erb +1 -1
- data/lib/generators/logidze/install/templates/migration.rb.erb +19 -232
- data/lib/generators/logidze/install/templates/migration_fx.rb.erb +41 -0
- data/lib/generators/logidze/model/model_generator.rb +49 -13
- data/lib/generators/logidze/model/templates/migration.rb.erb +57 -36
- data/lib/generators/logidze/model/triggers/logidze.sql +6 -0
- data/lib/logidze.rb +27 -14
- data/lib/logidze/history.rb +1 -10
- data/lib/logidze/ignore_log_data.rb +1 -4
- data/lib/logidze/model.rb +48 -35
- data/lib/logidze/version.rb +1 -1
- metadata +48 -73
- data/.gitattributes +0 -3
- data/.github/ISSUE_TEMPLATE.md +0 -20
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
- data/.gitignore +0 -40
- data/.rubocop.yml +0 -55
- data/.travis.yml +0 -46
- data/Gemfile +0 -15
- data/Rakefile +0 -28
- data/assets/pg_log_data_chart.png +0 -0
- data/bench/performance/README.md +0 -109
- data/bench/performance/diff_bench.rb +0 -38
- data/bench/performance/insert_bench.rb +0 -22
- data/bench/performance/memory_profile.rb +0 -56
- data/bench/performance/setup.rb +0 -315
- data/bench/performance/update_bench.rb +0 -38
- data/bench/triggers/Makefile +0 -56
- data/bench/triggers/Readme.md +0 -58
- data/bench/triggers/bench.sql +0 -6
- data/bench/triggers/hstore_trigger_setup.sql +0 -38
- data/bench/triggers/jsonb_minus_2_setup.sql +0 -47
- data/bench/triggers/jsonb_minus_setup.sql +0 -49
- data/bench/triggers/keys2_trigger_setup.sql +0 -44
- data/bench/triggers/keys_trigger_setup.sql +0 -50
- data/bin/console +0 -8
- data/bin/setup +0 -9
- data/gemfiles/rails42.gemfile +0 -6
- data/gemfiles/rails5.gemfile +0 -6
- data/gemfiles/rails52.gemfile +0 -6
- data/gemfiles/rails6.gemfile +0 -6
- data/gemfiles/railsmaster.gemfile +0 -7
- data/lib/logidze/ignore_log_data/ignored_columns.rb +0 -46
- data/lib/logidze/migration.rb +0 -20
- data/logidze.gemspec +0 -41
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "benchmark/ips"
|
4
|
-
require "./setup"
|
5
|
-
|
6
|
-
params = {
|
7
|
-
age: Faker::Number.number(2),
|
8
|
-
email: Faker::Internet.email
|
9
|
-
}
|
10
|
-
|
11
|
-
params2 = {
|
12
|
-
email: Faker::Internet.email,
|
13
|
-
position: Faker::Number.number(3),
|
14
|
-
name: Faker::Name.name,
|
15
|
-
age: Faker::Number.number(2),
|
16
|
-
bio: Faker::Lorem.paragraph
|
17
|
-
}
|
18
|
-
|
19
|
-
LogidzeBench.cleanup
|
20
|
-
LogidzeBench.populate
|
21
|
-
|
22
|
-
Benchmark.ips do |x|
|
23
|
-
x.report("PT UPDATE #1") do
|
24
|
-
User.random.update!(params)
|
25
|
-
end
|
26
|
-
|
27
|
-
x.report("Logidze UPDATE #1") do
|
28
|
-
LogidzeUser.random.update!(params)
|
29
|
-
end
|
30
|
-
|
31
|
-
x.report("PT UPDATE #2") do
|
32
|
-
User.random.update!(params2)
|
33
|
-
end
|
34
|
-
|
35
|
-
x.report("Logidze UPDATE #2") do
|
36
|
-
LogidzeUser.random.update!(params2)
|
37
|
-
end
|
38
|
-
end
|
data/bench/triggers/Makefile
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
# Database name
|
2
|
-
ifndef DB
|
3
|
-
DB = logidze_bench
|
4
|
-
endif
|
5
|
-
|
6
|
-
# Transactions count
|
7
|
-
ifndef T
|
8
|
-
T = 10000
|
9
|
-
endif
|
10
|
-
|
11
|
-
all: plain hstore jsonb jsonb2 keys keys2
|
12
|
-
|
13
|
-
setup:
|
14
|
-
createdb $(DB) -w
|
15
|
-
psql -q -d $(DB) -c 'CREATE EXTENSION IF NOT EXISTS hstore;'
|
16
|
-
|
17
|
-
plain:
|
18
|
-
$(info )
|
19
|
-
$(info ====== [START] Update without triggers ======)
|
20
|
-
pgbench -i -q $(DB)
|
21
|
-
pgbench -f bench.sql -t $(T) -r $(DB)
|
22
|
-
|
23
|
-
hstore:
|
24
|
-
$(info )
|
25
|
-
$(info ====== [START] Update with hstore-based triggers ======)
|
26
|
-
pgbench -i -q $(DB)
|
27
|
-
psql -q -d $(DB) -f hstore_trigger_setup.sql
|
28
|
-
pgbench -f bench.sql -t $(T) -r $(DB)
|
29
|
-
|
30
|
-
jsonb:
|
31
|
-
$(info )
|
32
|
-
$(info ====== [START] Update with jsonb-minus triggers ======)
|
33
|
-
pgbench -i -q $(DB)
|
34
|
-
psql -q -d $(DB) -f jsonb_minus_setup.sql
|
35
|
-
pgbench -f bench.sql -t $(T) -r $(DB)
|
36
|
-
|
37
|
-
jsonb2:
|
38
|
-
$(info )
|
39
|
-
$(info ====== [START] Update with jsonb-minus triggers ======)
|
40
|
-
pgbench -i -q $(DB)
|
41
|
-
psql -q -d $(DB) -f jsonb_minus_2_setup.sql
|
42
|
-
pgbench -f bench.sql -t $(T) -r $(DB)
|
43
|
-
|
44
|
-
keys:
|
45
|
-
$(info )
|
46
|
-
$(info ====== [START] Update with loop thru keys triggers (v1) ======)
|
47
|
-
pgbench -i -q $(DB)
|
48
|
-
psql -q -d $(DB) -f keys_trigger_setup.sql
|
49
|
-
pgbench -f bench.sql -t $(T) -r $(DB)
|
50
|
-
|
51
|
-
keys2:
|
52
|
-
$(info )
|
53
|
-
$(info ====== [START] Update with loop thru keys triggers (v2) ======)
|
54
|
-
pgbench -i -q $(DB)
|
55
|
-
psql -q -d $(DB) -f keys2_trigger_setup.sql
|
56
|
-
pgbench -f bench.sql -t $(T) -r $(DB)
|
data/bench/triggers/Readme.md
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
# Triggers benchmarks
|
2
|
-
|
3
|
-
This benchmark uses standard _pg\_bench_ table `pgbench_accounts`.
|
4
|
-
We consider several approaches for calculating records diff: one uses _hstore_ extension, two uses jsonb functions and two others iterate through record fields.
|
5
|
-
|
6
|
-
# Usage
|
7
|
-
|
8
|
-
Create database:
|
9
|
-
|
10
|
-
```sh
|
11
|
-
make setup
|
12
|
-
```
|
13
|
-
|
14
|
-
You can provide database name by `DB` variable (defaults to "logidze_bench").
|
15
|
-
|
16
|
-
Run all benchmarks:
|
17
|
-
|
18
|
-
```sh
|
19
|
-
make
|
20
|
-
```
|
21
|
-
|
22
|
-
or separate benchmark:
|
23
|
-
|
24
|
-
```sh
|
25
|
-
make hstore
|
26
|
-
|
27
|
-
make jsonb
|
28
|
-
|
29
|
-
make jsonb2
|
30
|
-
|
31
|
-
make keys
|
32
|
-
|
33
|
-
make keys2
|
34
|
-
|
35
|
-
# Raw update, no triggers
|
36
|
-
make plain
|
37
|
-
```
|
38
|
-
|
39
|
-
You can specify the number of transactions by `T` variable (defaults to 10000):
|
40
|
-
|
41
|
-
```sh
|
42
|
-
make T=1000000
|
43
|
-
```
|
44
|
-
|
45
|
-
# Results
|
46
|
-
|
47
|
-
The benchmark shows that hstore variant is the most efficient (running on MacPro 2013, 2.4 GHz Core i5, 4GB, SSD, 1 million transactions per test):
|
48
|
-
|
49
|
-
|Mode | TPS | Statement latency (ms) |
|
50
|
-
|--------|------|------------------------|
|
51
|
-
| plain | 3628 | 0.113 |
|
52
|
-
| hstore | 3015 | 0.168 |
|
53
|
-
| jsonb | 1647 | 0.363 |
|
54
|
-
| jsonb2 | 1674 | 0.354 |
|
55
|
-
| keys | 2355 | 0.219 |
|
56
|
-
| keys2 | 2542 | 0.210 |
|
57
|
-
|
58
|
-
_Logidze_ uses hstore variant.
|
data/bench/triggers/bench.sql
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
CREATE OR REPLACE FUNCTION hstore_logger() RETURNS TRIGGER AS $body$
|
2
|
-
DECLARE
|
3
|
-
changes_h jsonb;
|
4
|
-
size integer;
|
5
|
-
buffer jsonb;
|
6
|
-
BEGIN
|
7
|
-
size := jsonb_array_length(NEW.log);
|
8
|
-
|
9
|
-
changes_h := hstore_to_jsonb_loose(
|
10
|
-
hstore(NEW.*) - hstore(OLD.*)
|
11
|
-
);
|
12
|
-
|
13
|
-
NEW.log := jsonb_set(
|
14
|
-
NEW.log,
|
15
|
-
ARRAY[size::text],
|
16
|
-
jsonb_build_object(
|
17
|
-
'ts',
|
18
|
-
extract(epoch from now())::int,
|
19
|
-
'i',
|
20
|
-
(NEW.log#>>ARRAY[(size - 1)::text, 'i'])::int + 1,
|
21
|
-
'd',
|
22
|
-
changes_h
|
23
|
-
),
|
24
|
-
true
|
25
|
-
);
|
26
|
-
return NEW;
|
27
|
-
END;
|
28
|
-
$body$
|
29
|
-
LANGUAGE plpgsql;
|
30
|
-
|
31
|
-
|
32
|
-
ALTER TABLE pgbench_accounts ADD COLUMN log jsonb DEFAULT '[]' NOT NULL;
|
33
|
-
|
34
|
-
UPDATE pgbench_accounts SET log = to_jsonb(ARRAY[json_build_object('i', 0)])::jsonb;
|
35
|
-
|
36
|
-
CREATE TRIGGER hstore_log_accounts
|
37
|
-
BEFORE UPDATE ON pgbench_accounts FOR EACH ROW
|
38
|
-
EXECUTE PROCEDURE hstore_logger();
|
@@ -1,47 +0,0 @@
|
|
1
|
-
CREATE OR REPLACE FUNCTION jsonb_minus(arg1 jsonb, arg2 jsonb) RETURNS jsonb
|
2
|
-
AS $$
|
3
|
-
|
4
|
-
SELECT
|
5
|
-
COALESCE(json_object_agg(key, value), '{}')::jsonb
|
6
|
-
FROM
|
7
|
-
jsonb_each(arg1)
|
8
|
-
WHERE NOT jsonb_build_object(key, value) <@ arg2;
|
9
|
-
|
10
|
-
$$ LANGUAGE SQL;
|
11
|
-
|
12
|
-
CREATE OR REPLACE FUNCTION jsonb_minus_2_logger() RETURNS TRIGGER AS $body$
|
13
|
-
DECLARE
|
14
|
-
changes_h jsonb;
|
15
|
-
size integer;
|
16
|
-
buffer jsonb;
|
17
|
-
BEGIN
|
18
|
-
size := jsonb_array_length(NEW.log);
|
19
|
-
|
20
|
-
changes_h := jsonb_minus(row_to_json(OLD)::jsonb, row_to_json(NEW)::jsonb);
|
21
|
-
|
22
|
-
NEW.log := jsonb_set(
|
23
|
-
NEW.log,
|
24
|
-
ARRAY[size::text],
|
25
|
-
jsonb_build_object(
|
26
|
-
'ts',
|
27
|
-
extract(epoch from now())::int,
|
28
|
-
'i',
|
29
|
-
(NEW.log#>>ARRAY[(size - 1)::text, 'i'])::int + 1,
|
30
|
-
'd',
|
31
|
-
changes_h
|
32
|
-
),
|
33
|
-
true
|
34
|
-
);
|
35
|
-
return NEW;
|
36
|
-
END;
|
37
|
-
$body$
|
38
|
-
LANGUAGE plpgsql;
|
39
|
-
|
40
|
-
|
41
|
-
ALTER TABLE pgbench_accounts ADD COLUMN log jsonb DEFAULT '[]' NOT NULL;
|
42
|
-
|
43
|
-
UPDATE pgbench_accounts SET log = to_jsonb(ARRAY[json_build_object('i', 0)])::jsonb;
|
44
|
-
|
45
|
-
CREATE TRIGGER hstore_log_accounts
|
46
|
-
BEFORE UPDATE ON pgbench_accounts FOR EACH ROW
|
47
|
-
EXECUTE PROCEDURE jsonb_minus_2_logger();
|
@@ -1,49 +0,0 @@
|
|
1
|
-
CREATE OR REPLACE FUNCTION jsonb_minus(arg1 jsonb, arg2 jsonb) RETURNS jsonb
|
2
|
-
AS $$
|
3
|
-
|
4
|
-
SELECT
|
5
|
-
COALESCE(json_object_agg(key, value), '{}')::jsonb
|
6
|
-
FROM
|
7
|
-
jsonb_each(arg1)
|
8
|
-
WHERE
|
9
|
-
arg1 -> key <> arg2 -> key
|
10
|
-
OR arg2 -> key IS NULL;
|
11
|
-
|
12
|
-
$$ LANGUAGE SQL;
|
13
|
-
|
14
|
-
CREATE OR REPLACE FUNCTION jsonb_minus_logger() RETURNS TRIGGER AS $body$
|
15
|
-
DECLARE
|
16
|
-
changes_h jsonb;
|
17
|
-
size integer;
|
18
|
-
buffer jsonb;
|
19
|
-
BEGIN
|
20
|
-
size := jsonb_array_length(NEW.log);
|
21
|
-
|
22
|
-
changes_h := jsonb_minus(row_to_json(OLD)::jsonb, row_to_json(NEW)::jsonb);
|
23
|
-
|
24
|
-
NEW.log := jsonb_set(
|
25
|
-
NEW.log,
|
26
|
-
ARRAY[size::text],
|
27
|
-
jsonb_build_object(
|
28
|
-
'ts',
|
29
|
-
extract(epoch from now())::int,
|
30
|
-
'i',
|
31
|
-
(NEW.log#>>ARRAY[(size - 1)::text, 'i'])::int + 1,
|
32
|
-
'd',
|
33
|
-
changes_h
|
34
|
-
),
|
35
|
-
true
|
36
|
-
);
|
37
|
-
return NEW;
|
38
|
-
END;
|
39
|
-
$body$
|
40
|
-
LANGUAGE plpgsql;
|
41
|
-
|
42
|
-
|
43
|
-
ALTER TABLE pgbench_accounts ADD COLUMN log jsonb DEFAULT '[]' NOT NULL;
|
44
|
-
|
45
|
-
UPDATE pgbench_accounts SET log = to_jsonb(ARRAY[json_build_object('i', 0)])::jsonb;
|
46
|
-
|
47
|
-
CREATE TRIGGER hstore_log_accounts
|
48
|
-
BEFORE UPDATE ON pgbench_accounts FOR EACH ROW
|
49
|
-
EXECUTE PROCEDURE jsonb_minus_logger();
|
@@ -1,44 +0,0 @@
|
|
1
|
-
CREATE OR REPLACE FUNCTION keys_2_logger() RETURNS TRIGGER AS $body$
|
2
|
-
DECLARE
|
3
|
-
size integer;
|
4
|
-
old_j jsonb;
|
5
|
-
changes_j jsonb;
|
6
|
-
item record;
|
7
|
-
BEGIN
|
8
|
-
size := jsonb_array_length(NEW.log);
|
9
|
-
old_j := to_jsonb(OLD);
|
10
|
-
changes_j := to_jsonb(NEW);
|
11
|
-
|
12
|
-
FOR item in SELECT key as k, value as v FROM jsonb_each(old_j)
|
13
|
-
LOOP
|
14
|
-
IF changes_j->item.k = item.v THEN
|
15
|
-
changes_j := changes_j - item.k;
|
16
|
-
END IF;
|
17
|
-
END LOOP;
|
18
|
-
|
19
|
-
NEW.log := jsonb_set(
|
20
|
-
NEW.log,
|
21
|
-
ARRAY[size::text],
|
22
|
-
jsonb_build_object(
|
23
|
-
'ts',
|
24
|
-
extract(epoch from now())::int,
|
25
|
-
'i',
|
26
|
-
(NEW.log#>>ARRAY[(size - 1)::text, 'i'])::int + 1,
|
27
|
-
'd',
|
28
|
-
changes_j
|
29
|
-
),
|
30
|
-
true
|
31
|
-
);
|
32
|
-
return NEW;
|
33
|
-
END;
|
34
|
-
$body$
|
35
|
-
LANGUAGE plpgsql;
|
36
|
-
|
37
|
-
|
38
|
-
ALTER TABLE pgbench_accounts ADD COLUMN log jsonb DEFAULT '[]' NOT NULL;
|
39
|
-
|
40
|
-
UPDATE pgbench_accounts SET log = to_jsonb(ARRAY[json_build_object('i', 0)])::jsonb;
|
41
|
-
|
42
|
-
CREATE TRIGGER keys_log_accounts
|
43
|
-
BEFORE UPDATE ON pgbench_accounts FOR EACH ROW
|
44
|
-
EXECUTE PROCEDURE keys_2_logger();
|
@@ -1,50 +0,0 @@
|
|
1
|
-
CREATE OR REPLACE FUNCTION keys_logger() RETURNS TRIGGER AS $body$
|
2
|
-
DECLARE
|
3
|
-
changes_h jsonb;
|
4
|
-
size integer;
|
5
|
-
old_j jsonb;
|
6
|
-
new_j jsonb;
|
7
|
-
item record;
|
8
|
-
BEGIN
|
9
|
-
size := jsonb_array_length(NEW.log);
|
10
|
-
old_j := to_jsonb(OLD);
|
11
|
-
new_j := to_jsonb(NEW);
|
12
|
-
changes_h := '{}'::jsonb;
|
13
|
-
|
14
|
-
FOR item in SELECT key as k, value as v FROM jsonb_each(new_j)
|
15
|
-
LOOP
|
16
|
-
IF item.v <> jsonb_extract_path(old_j, item.k) THEN
|
17
|
-
changes_h := jsonb_set(
|
18
|
-
changes_h,
|
19
|
-
ARRAY[item.k],
|
20
|
-
item.v
|
21
|
-
);
|
22
|
-
END IF;
|
23
|
-
END LOOP;
|
24
|
-
|
25
|
-
NEW.log := jsonb_set(
|
26
|
-
NEW.log,
|
27
|
-
ARRAY[size::text],
|
28
|
-
jsonb_build_object(
|
29
|
-
'ts',
|
30
|
-
extract(epoch from now())::int,
|
31
|
-
'i',
|
32
|
-
(NEW.log#>>ARRAY[(size - 1)::text, 'i'])::int + 1,
|
33
|
-
'd',
|
34
|
-
changes_h
|
35
|
-
),
|
36
|
-
true
|
37
|
-
);
|
38
|
-
return NEW;
|
39
|
-
END;
|
40
|
-
$body$
|
41
|
-
LANGUAGE plpgsql;
|
42
|
-
|
43
|
-
|
44
|
-
ALTER TABLE pgbench_accounts ADD COLUMN log jsonb DEFAULT '[]' NOT NULL;
|
45
|
-
|
46
|
-
UPDATE pgbench_accounts SET log = to_jsonb(ARRAY[json_build_object('i', 0)])::jsonb;
|
47
|
-
|
48
|
-
CREATE TRIGGER keys_log_accounts
|
49
|
-
BEFORE UPDATE ON pgbench_accounts FOR EACH ROW
|
50
|
-
EXECUTE PROCEDURE keys_logger();
|
data/bin/console
DELETED
data/bin/setup
DELETED
data/gemfiles/rails42.gemfile
DELETED
data/gemfiles/rails5.gemfile
DELETED
data/gemfiles/rails52.gemfile
DELETED