pg_party 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +26 -0
- data/lib/pg_party.rb +26 -0
- data/lib/pg_party/adapter_decorator.rb +2 -3
- data/lib/pg_party/cache.rb +50 -16
- data/lib/pg_party/config.rb +16 -0
- data/lib/pg_party/hacks/database_tasks.rb +31 -0
- data/lib/pg_party/model/shared_methods.rb +10 -2
- data/lib/pg_party/model_decorator.rb +7 -20
- data/lib/pg_party/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5996516e7f3b76373b6fc4f9a29c9e31f48d9edb086b7e0f217031f0ed3707ea
|
4
|
+
data.tar.gz: d4e5d12b2bdb80ab928153efe9ec49e11b7e6e413f0a0edbeddfe2f24a7d4276
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fac73650e2065c300fabb4ad826e59b8877310033490778b81835d28f297135796befec74a3ca5f6f6d5b3204f65a0f6686d58875df53e1332d96b6ef09edbac
|
7
|
+
data.tar.gz: 0fec1b06196dc2fecd95464694ae1d9742fb72687ac676b5a6def977b1a965a2f137b70edb85310f273237d8a7f6370aaa6a193d5c926487e30710d53de720f0
|
data/README.md
CHANGED
@@ -48,6 +48,32 @@ $ gem install pg_party
|
|
48
48
|
Note that the gemspec does not require `pg`, as some model methods _may_ work for other databases.
|
49
49
|
Migration methods will be unavailable unless `pg` is installed.
|
50
50
|
|
51
|
+
## Configuration
|
52
|
+
|
53
|
+
These values can be accessed and set via `PgParty.config` and `PgParty.configure`.
|
54
|
+
|
55
|
+
- `caching`
|
56
|
+
- Whether to cache currently attached partitions and anonymous model classes
|
57
|
+
- Default: `true`
|
58
|
+
- `caching_ttl`
|
59
|
+
- Length of time (in seconds) that cache entries are considered valid
|
60
|
+
- Default: `-1` (never expire cache entries)
|
61
|
+
- `schema_exclude_partitions`
|
62
|
+
- Whether to exclude child partitions in `rake db:structure:dump` (supported in ActiveRecord 5.2+)
|
63
|
+
- Default: `true`
|
64
|
+
|
65
|
+
Note that caching is done in-memory for each process of an application. Attaching / detaching partitions _will_ clear the cache, but only for the process that initiated the request. For multi-process web servers, it is recommended to use a TTL or disable caching entirely.
|
66
|
+
|
67
|
+
### Example
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
# in a Rails initializer
|
71
|
+
PgParty.configure do |c|
|
72
|
+
c.caching_ttl = 60
|
73
|
+
c.schema_exclude_partitions = false
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
51
77
|
## Usage
|
52
78
|
|
53
79
|
### Migrations
|
data/lib/pg_party.rb
CHANGED
@@ -1,8 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "pg_party/version"
|
4
|
+
require "pg_party/config"
|
5
|
+
require "pg_party/cache"
|
4
6
|
require "active_support"
|
5
7
|
|
8
|
+
module PgParty
|
9
|
+
@config = Config.new
|
10
|
+
@cache = Cache.new
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_reader :config, :cache
|
14
|
+
|
15
|
+
def configure(&blk)
|
16
|
+
blk.call(config)
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset
|
20
|
+
@config = Config.new
|
21
|
+
@cache = Cache.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
6
26
|
ActiveSupport.on_load(:active_record) do
|
7
27
|
require "pg_party/model/methods"
|
8
28
|
|
@@ -20,6 +40,12 @@ ActiveSupport.on_load(:active_record) do
|
|
20
40
|
PgParty::Hacks::SchemaCache
|
21
41
|
)
|
22
42
|
|
43
|
+
require "pg_party/hacks/database_tasks"
|
44
|
+
|
45
|
+
ActiveRecord::Tasks::DatabaseTasks.extend(
|
46
|
+
PgParty::Hacks::DatabaseTasks
|
47
|
+
)
|
48
|
+
|
23
49
|
begin
|
24
50
|
require "active_record/connection_adapters/postgresql_adapter"
|
25
51
|
require "pg_party/adapter/postgresql_methods"
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "digest"
|
4
|
-
require "pg_party/cache"
|
5
4
|
|
6
5
|
module PgParty
|
7
6
|
class AdapterDecorator < SimpleDelegator
|
@@ -61,7 +60,7 @@ module PgParty
|
|
61
60
|
DETACH PARTITION #{quote_table_name(child_table_name)}
|
62
61
|
SQL
|
63
62
|
|
64
|
-
PgParty
|
63
|
+
PgParty.cache.clear!
|
65
64
|
end
|
66
65
|
|
67
66
|
private
|
@@ -118,7 +117,7 @@ module PgParty
|
|
118
117
|
FOR VALUES #{constraint_clause}
|
119
118
|
SQL
|
120
119
|
|
121
|
-
PgParty
|
120
|
+
PgParty.cache.clear!
|
122
121
|
end
|
123
122
|
|
124
123
|
# Rails 5.2 now returns boolean literals
|
data/lib/pg_party/cache.rb
CHANGED
@@ -6,32 +6,66 @@ module PgParty
|
|
6
6
|
class Cache
|
7
7
|
LOCK = Mutex.new
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
def initialize
|
10
|
+
# automatically initialize a new hash when
|
11
|
+
# accessing an object id that doesn't exist
|
12
|
+
@store = Hash.new { |h, k| h[k] = { models: {}, partitions: nil } }
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear!
|
16
|
+
LOCK.synchronize { @store.clear }
|
17
|
+
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def fetch_model(key, child_table, &block)
|
22
|
+
return block.call unless caching_enabled?
|
23
|
+
|
24
|
+
LOCK.synchronize { fetch_value(@store[key][:models], child_table.to_sym, block) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_partitions(key, &block)
|
28
|
+
return block.call unless caching_enabled?
|
29
|
+
|
30
|
+
LOCK.synchronize { fetch_value(@store[key], :partitions, block) }
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def caching_enabled?
|
36
|
+
PgParty.config.caching
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_value(subhash, key, block)
|
40
|
+
entry = subhash[key]
|
12
41
|
|
13
|
-
|
42
|
+
if entry.nil? || entry.expired?
|
43
|
+
entry = Entry.new(block.call)
|
44
|
+
subhash[key] = entry
|
14
45
|
end
|
15
46
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
47
|
+
entry.value
|
48
|
+
end
|
49
|
+
|
50
|
+
class Entry
|
51
|
+
attr_reader :value
|
52
|
+
|
53
|
+
def initialize(value)
|
54
|
+
@value = value
|
55
|
+
@timestamp = Time.now
|
20
56
|
end
|
21
57
|
|
22
|
-
def
|
23
|
-
|
24
|
-
store[key][:partitions] ||= block.call
|
25
|
-
end
|
58
|
+
def expired?
|
59
|
+
ttl.positive? && Time.now - @timestamp > ttl
|
26
60
|
end
|
27
61
|
|
28
62
|
private
|
29
63
|
|
30
|
-
def
|
31
|
-
|
32
|
-
# accessing an object id that doesn't exist
|
33
|
-
@store ||= Hash.new { |h, k| h[k] = { models: {}, partitions: nil } }
|
64
|
+
def ttl
|
65
|
+
PgParty.config.caching_ttl
|
34
66
|
end
|
35
67
|
end
|
68
|
+
|
69
|
+
private_constant :Entry
|
36
70
|
end
|
37
71
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgParty
|
4
|
+
class Config
|
5
|
+
attr_accessor \
|
6
|
+
:caching,
|
7
|
+
:caching_ttl,
|
8
|
+
:schema_exclude_partitions
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@caching = true
|
12
|
+
@caching_ttl = -1
|
13
|
+
@schema_exclude_partitions = true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgParty
|
4
|
+
module Hacks
|
5
|
+
module DatabaseTasks
|
6
|
+
def structure_dump(*)
|
7
|
+
old_ignore_list = ActiveRecord::SchemaDumper.ignore_tables
|
8
|
+
|
9
|
+
if PgParty.config.schema_exclude_partitions
|
10
|
+
new_ignore_list = partitions.map { |table| "*.#{table}" }
|
11
|
+
else
|
12
|
+
new_ignore_list = []
|
13
|
+
end
|
14
|
+
|
15
|
+
ActiveRecord::SchemaDumper.ignore_tables = old_ignore_list + new_ignore_list
|
16
|
+
|
17
|
+
super
|
18
|
+
ensure
|
19
|
+
ActiveRecord::SchemaDumper.ignore_tables = old_ignore_list
|
20
|
+
end
|
21
|
+
|
22
|
+
def partitions
|
23
|
+
ActiveRecord::Base.connection.select_values(
|
24
|
+
"SELECT DISTINCT inhrelid::regclass::text FROM pg_inherits"
|
25
|
+
)
|
26
|
+
rescue
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -6,11 +6,19 @@ module PgParty
|
|
6
6
|
module Model
|
7
7
|
module SharedMethods
|
8
8
|
def reset_primary_key
|
9
|
-
|
9
|
+
if self != base_class
|
10
|
+
base_class.primary_key
|
11
|
+
elsif partition_name = partitions.first
|
12
|
+
in_partition(partition_name).get_primary_key(base_class.name)
|
13
|
+
else
|
14
|
+
get_primary_key(base_class.name)
|
15
|
+
end
|
10
16
|
end
|
11
17
|
|
12
18
|
def table_exists?
|
13
|
-
|
19
|
+
target_table = partitions.first || table_name
|
20
|
+
|
21
|
+
connection.schema_cache.data_source_exists?(target_table)
|
14
22
|
end
|
15
23
|
|
16
24
|
def partitions
|
@@ -1,27 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "pg_party/cache"
|
4
|
-
|
5
3
|
module PgParty
|
6
4
|
class ModelDecorator < SimpleDelegator
|
7
|
-
def partition_primary_key
|
8
|
-
if self != base_class
|
9
|
-
base_class.primary_key
|
10
|
-
elsif partition_name = partitions.first
|
11
|
-
in_partition(partition_name).get_primary_key(base_class.name)
|
12
|
-
else
|
13
|
-
get_primary_key(base_class.name)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def partition_table_exists?
|
18
|
-
target_table = partitions.first || table_name
|
19
|
-
|
20
|
-
connection.schema_cache.data_source_exists?(target_table)
|
21
|
-
end
|
22
|
-
|
23
5
|
def in_partition(child_table_name)
|
24
|
-
PgParty
|
6
|
+
PgParty.cache.fetch_model(cache_key, child_table_name) do
|
25
7
|
Class.new(__getobj__) do
|
26
8
|
self.table_name = child_table_name
|
27
9
|
|
@@ -41,6 +23,11 @@ module PgParty
|
|
41
23
|
def self.new(*args, &blk)
|
42
24
|
superclass.new(*args, &blk)
|
43
25
|
end
|
26
|
+
|
27
|
+
# to avoid unnecessary db lookups
|
28
|
+
def self.partitions
|
29
|
+
[]
|
30
|
+
end
|
44
31
|
end
|
45
32
|
end
|
46
33
|
end
|
@@ -76,7 +63,7 @@ module PgParty
|
|
76
63
|
end
|
77
64
|
|
78
65
|
def partitions
|
79
|
-
PgParty
|
66
|
+
PgParty.cache.fetch_partitions(cache_key) do
|
80
67
|
connection.select_values(<<-SQL)
|
81
68
|
SELECT pg_inherits.inhrelid::regclass::text
|
82
69
|
FROM pg_tables
|
data/lib/pg_party/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_party
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Krage
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -219,6 +219,8 @@ files:
|
|
219
219
|
- lib/pg_party/adapter/postgresql_methods.rb
|
220
220
|
- lib/pg_party/adapter_decorator.rb
|
221
221
|
- lib/pg_party/cache.rb
|
222
|
+
- lib/pg_party/config.rb
|
223
|
+
- lib/pg_party/hacks/database_tasks.rb
|
222
224
|
- lib/pg_party/hacks/schema_cache.rb
|
223
225
|
- lib/pg_party/model/list_methods.rb
|
224
226
|
- lib/pg_party/model/methods.rb
|