messaging 3.6.2 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|