messaging 3.6.2 → 3.7.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/lib/messaging/adapters/postgres/categories/row.rb +30 -0
- data/lib/messaging/adapters/postgres/categories.rb +28 -13
- data/lib/messaging/adapters/postgres/category.rb +7 -21
- data/lib/messaging/adapters/postgres/category_with_partitions.rb +89 -0
- data/lib/messaging/adapters/postgres/store.rb +9 -0
- data/lib/messaging/adapters/test/category.rb +0 -4
- data/lib/messaging/message.rb +4 -0
- data/lib/messaging/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: 860c436d2faab463d40144358859eb282aa352370ffb2c725cfa292c267eb959
|
4
|
+
data.tar.gz: cf39e2bf8a10ebe84cfe09d9e74d0fa9bce654803478865b47601b5ada04d404
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0751658ec0a77cc8fd925963c1530c05ee31cb458c8f773a8aba7dd333569e90547945d52dda5cbee21e0f0668cf6d7909cb2f2bd598815a4145cff375233573'
|
7
|
+
data.tar.gz: 0abc8205f0f400f5cade71abe7f075a96338beb44c8559c79b5603f478ddbd8dec7dee4a7ca56784e2be2f10238f16667fd2d68a7e7e54b36caa0529fd62fd95
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Adapters
|
3
|
+
class Postgres
|
4
|
+
class Categories
|
5
|
+
class Row
|
6
|
+
extend Dry::Initializer
|
7
|
+
|
8
|
+
param :table_name
|
9
|
+
param :type
|
10
|
+
param :expression
|
11
|
+
|
12
|
+
def category_class
|
13
|
+
return CategoryWithPartitions if type == 'p'
|
14
|
+
|
15
|
+
Category
|
16
|
+
end
|
17
|
+
|
18
|
+
def category_name
|
19
|
+
regexp = /FOR VALUES IN \(\'(.*)\'\)/
|
20
|
+
expression.match(regexp)[1]
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_category
|
24
|
+
category_class.new(category_name, table_name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -5,9 +5,7 @@ module Messaging
|
|
5
5
|
include Enumerable
|
6
6
|
|
7
7
|
def each
|
8
|
-
all_categories.each
|
9
|
-
yield Category.new(name)
|
10
|
-
end
|
8
|
+
all_categories.each { |c| yield c }
|
11
9
|
end
|
12
10
|
|
13
11
|
# Get a category by name
|
@@ -16,10 +14,7 @@ module Messaging
|
|
16
14
|
# @return [nil] if no category exists with the given name
|
17
15
|
# @return [Category]
|
18
16
|
def [](name)
|
19
|
-
|
20
|
-
return unless category
|
21
|
-
|
22
|
-
Category.new(category)
|
17
|
+
all_categories.find { |c| c.name == name }
|
23
18
|
end
|
24
19
|
|
25
20
|
# Creates a table partition for the given category
|
@@ -27,20 +22,36 @@ module Messaging
|
|
27
22
|
# @param name [String] the name of the category
|
28
23
|
# @return [Category]
|
29
24
|
def create(name)
|
30
|
-
table_name =
|
25
|
+
table_name = Category.table_name_for(name)
|
31
26
|
sql = <<~SQL
|
32
27
|
CREATE TABLE messaging.#{table_name}
|
33
28
|
PARTITION OF messaging.messages FOR VALUES IN ('#{name}');
|
34
29
|
SQL
|
35
30
|
connection.execute sql
|
36
|
-
Category.new(name)
|
31
|
+
Category.new(name, table_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Creates a table partition for the given category
|
35
|
+
# that in turn is partitioned based on created_at
|
36
|
+
#
|
37
|
+
# @param name [String] the name of the category
|
38
|
+
# @return [CategoryWithPartitions]
|
39
|
+
def create_and_partition_by_day(name)
|
40
|
+
table_name = Category.table_name_for(name)
|
41
|
+
sql = <<~SQL
|
42
|
+
CREATE TABLE messaging.#{table_name}
|
43
|
+
PARTITION OF messaging.messages FOR VALUES IN ('#{name}')
|
44
|
+
PARTITION BY RANGE (created_at);
|
45
|
+
SQL
|
46
|
+
connection.execute sql
|
47
|
+
CategoryWithPartitions.new(name, table_name)
|
37
48
|
end
|
38
49
|
|
39
50
|
# Drops the table partition (including all messages) for the given category
|
40
51
|
#
|
41
52
|
# @param name [String] the name of the category
|
42
53
|
def drop(name)
|
43
|
-
table_name =
|
54
|
+
table_name = Category.table_name_for(name)
|
44
55
|
sql = <<~SQL
|
45
56
|
drop TABLE messaging.#{table_name}
|
46
57
|
SQL
|
@@ -55,15 +66,19 @@ module Messaging
|
|
55
66
|
|
56
67
|
def fetch_categories
|
57
68
|
sql = <<~SQL
|
58
|
-
SELECT child.relname AS category
|
69
|
+
SELECT child.relname AS category,
|
70
|
+
child.relkind AS category_type,
|
71
|
+
pg_get_expr(child.relpartbound, child.oid, true) AS expression
|
59
72
|
FROM pg_inherits
|
60
73
|
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
|
61
74
|
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
|
62
75
|
JOIN pg_namespace ON pg_namespace.oid = parent.relnamespace
|
63
|
-
WHERE pg_namespace.nspname = 'messaging'
|
76
|
+
WHERE pg_namespace.nspname = 'messaging'
|
77
|
+
AND parent.relname = 'messages'
|
78
|
+
AND child.relkind in ('r', 'p')
|
64
79
|
ORDER BY category
|
65
80
|
SQL
|
66
|
-
connection.
|
81
|
+
connection.select_rows(sql).map { |r| Row.new(*r).to_category }
|
67
82
|
end
|
68
83
|
|
69
84
|
def connection
|
@@ -2,16 +2,13 @@ module Messaging
|
|
2
2
|
module Adapters
|
3
3
|
class Postgres
|
4
4
|
class Category
|
5
|
-
|
6
|
-
attr_reader :name
|
5
|
+
extend Dry::Initializer
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def initialize(name)
|
14
|
-
@name = name
|
7
|
+
param :name
|
8
|
+
param :table_name, default: -> { self.class.table_name_for(name) }
|
9
|
+
|
10
|
+
def self.table_name_for(name)
|
11
|
+
name.parameterize(separator: '_')
|
15
12
|
end
|
16
13
|
|
17
14
|
# Access to all messages in the category sorted by created_at
|
@@ -20,19 +17,8 @@ module Messaging
|
|
20
17
|
SerializedMessage.where(stream_category: name).order(:created_at)
|
21
18
|
end
|
22
19
|
|
23
|
-
def messages_older_than(time)
|
24
|
-
messages.where('created_at < ?', time)
|
25
|
-
end
|
26
|
-
|
27
|
-
def delete_messages_older_than!(time)
|
28
|
-
SerializedMessage.transaction do
|
29
|
-
ActiveRecord::Base.connection.execute "SET LOCAL statement_timeout = '0'"
|
30
|
-
messages_older_than(time).delete_all
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
20
|
def inspect
|
35
|
-
"#<Category
|
21
|
+
"#<Category: #{name}>"
|
36
22
|
end
|
37
23
|
end
|
38
24
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Adapters
|
3
|
+
class Postgres
|
4
|
+
class CategoryWithPartitions < Category
|
5
|
+
def add_partition(date:)
|
6
|
+
from, to = partition_range_for(date: date)
|
7
|
+
partition_name = partition_name_for(date: from)
|
8
|
+
|
9
|
+
return if partition_exists?(partition_name)
|
10
|
+
|
11
|
+
sql = <<~SQL
|
12
|
+
CREATE TABLE IF NOT EXISTS messaging.#{partition_name}
|
13
|
+
PARTITION OF messaging.#{table_name} FOR VALUES FROM ('#{from}') TO ('#{to}')
|
14
|
+
SQL
|
15
|
+
|
16
|
+
SerializedMessage.transaction do
|
17
|
+
AdvisoryTransactionLock.call key: partition_name
|
18
|
+
SerializedMessage.connection.execute sql
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates multiple partitions
|
23
|
+
#
|
24
|
+
# @param start_date [Date] from which date to create partitions
|
25
|
+
# @param days [Integer] how many days worth of partitions to create
|
26
|
+
def add_partitions(start_date:, days: 1)
|
27
|
+
first = start_date.to_date
|
28
|
+
last = first + (days - 1)
|
29
|
+
|
30
|
+
(first..last).each do |date|
|
31
|
+
add_partition(date: date)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Removes a partition including the included messages
|
36
|
+
#
|
37
|
+
# @param partition_name [String] the name of the partition to drop
|
38
|
+
def drop_partition(partition_name)
|
39
|
+
return unless partition_name.match?(/^#{table_name}_\d{4}_\d{2}_\d{2}$/)
|
40
|
+
|
41
|
+
SerializedMessage.connection.execute "drop TABLE messaging.#{partition_name}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Removes all partitions older than the given date
|
45
|
+
#
|
46
|
+
# @param date [Date] the cutoff date
|
47
|
+
def drop_partitions_older_than(date)
|
48
|
+
max_partition_name = partition_name_for(date: date)
|
49
|
+
partitions.select { |p| p < max_partition_name }.each do |p|
|
50
|
+
drop_partition(p)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def partition_name_for(date:)
|
55
|
+
"#{name}_%d_%02d_%02d" % [date.year, date.month, date.day]
|
56
|
+
end
|
57
|
+
|
58
|
+
def partition_range_for(date:)
|
59
|
+
from = date.to_time.beginning_of_day
|
60
|
+
to = from + 1.day
|
61
|
+
[from, to]
|
62
|
+
end
|
63
|
+
|
64
|
+
def partition_exists?(partition_name)
|
65
|
+
partitions.include? partition_name
|
66
|
+
end
|
67
|
+
|
68
|
+
def partitions
|
69
|
+
sql = <<~SQL
|
70
|
+
SELECT child.relname AS name
|
71
|
+
FROM pg_inherits
|
72
|
+
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
|
73
|
+
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
|
74
|
+
JOIN pg_namespace ON pg_namespace.oid = parent.relnamespace
|
75
|
+
WHERE pg_namespace.nspname = 'messaging'
|
76
|
+
AND parent.relname = '#{table_name}'
|
77
|
+
AND child.relkind = 'r'
|
78
|
+
ORDER BY name
|
79
|
+
SQL
|
80
|
+
SerializedMessage.connection.select_values(sql)
|
81
|
+
end
|
82
|
+
|
83
|
+
def inspect
|
84
|
+
"#<CategoryWithPartitions: #{name}>"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require_relative 'category'
|
2
|
+
require_relative 'category_with_partitions'
|
2
3
|
require_relative 'categories'
|
4
|
+
require_relative 'categories/row'
|
3
5
|
require_relative 'stream'
|
4
6
|
require_relative 'streams'
|
5
7
|
|
@@ -83,6 +85,13 @@ module Messaging
|
|
83
85
|
return message unless message.stream_name
|
84
86
|
|
85
87
|
SerializedMessage.create!(message: message).to_message
|
88
|
+
rescue ActiveRecord::StatementInvalid => e
|
89
|
+
category = message.category
|
90
|
+
raise e unless e.message.include?('no partition of relation')
|
91
|
+
raise e unless category || category.is_a?(CategoryWithPartitions)
|
92
|
+
|
93
|
+
category.add_partition(date: Date.today)
|
94
|
+
retry
|
86
95
|
end
|
87
96
|
end
|
88
97
|
end
|
data/lib/messaging/message.rb
CHANGED
data/lib/messaging/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: messaging
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bukowskis
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-02-
|
11
|
+
date: 2022-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -297,7 +297,9 @@ files:
|
|
297
297
|
- lib/messaging/adapters/postgres.rb
|
298
298
|
- lib/messaging/adapters/postgres/advisory_transaction_lock.rb
|
299
299
|
- lib/messaging/adapters/postgres/categories.rb
|
300
|
+
- lib/messaging/adapters/postgres/categories/row.rb
|
300
301
|
- lib/messaging/adapters/postgres/category.rb
|
302
|
+
- lib/messaging/adapters/postgres/category_with_partitions.rb
|
301
303
|
- lib/messaging/adapters/postgres/serialized_message.rb
|
302
304
|
- lib/messaging/adapters/postgres/store.rb
|
303
305
|
- lib/messaging/adapters/postgres/stream.rb
|