logidze 0.12.0 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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