pg_party 1.1.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11489588c1a9cccdbb8f39a73ebd3b91367c45640677211df0ff73afcb267598
4
- data.tar.gz: 0e2e6184b88e80afa186173c291ae3044976e1bd909f8d3501d35f780c024c09
3
+ metadata.gz: 5996516e7f3b76373b6fc4f9a29c9e31f48d9edb086b7e0f217031f0ed3707ea
4
+ data.tar.gz: d4e5d12b2bdb80ab928153efe9ec49e11b7e6e413f0a0edbeddfe2f24a7d4276
5
5
  SHA512:
6
- metadata.gz: 1dddde1408aeeee28b7bd0474c39483b3df2ccba68de294eeacc77421e8f8aae0508685f2c5926c695ab3c129d9ae0ffec45fca8d99be7ec8f15e4a7d7e5ead5
7
- data.tar.gz: dc26fa61e8c0d026e7c102c0100d275c9b1d1f5baf2880b6bca82fde7f3fa700d48ed30394e15d3d85fdf786d008148e8b90c23662c43914d4e852a80e976657
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::Cache.clear!
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::Cache.clear!
120
+ PgParty.cache.clear!
122
121
  end
123
122
 
124
123
  # Rails 5.2 now returns boolean literals
@@ -6,32 +6,66 @@ module PgParty
6
6
  class Cache
7
7
  LOCK = Mutex.new
8
8
 
9
- class << self
10
- def clear!
11
- LOCK.synchronize { store.clear }
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
- nil
42
+ if entry.nil? || entry.expired?
43
+ entry = Entry.new(block.call)
44
+ subhash[key] = entry
14
45
  end
15
46
 
16
- def fetch_model(key, child_table, &block)
17
- LOCK.synchronize do
18
- store[key][:models][child_table.to_sym] ||= block.call
19
- end
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 fetch_partitions(key, &block)
23
- LOCK.synchronize do
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 store
31
- # automatically initialize a new hash when
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
- PgParty::ModelDecorator.new(self).partition_primary_key
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
- PgParty::ModelDecorator.new(self).partition_table_exists?
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::Cache.fetch_model(cache_key, child_table_name) do
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::Cache.fetch_partitions(cache_key) do
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgParty
4
- VERSION = "1.1.0"
4
+ VERSION = "1.2.0"
5
5
  end
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.1.0
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-09-07 00:00:00.000000000 Z
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