umbrellio-utils 1.13.2 → 1.14.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/Gemfile.lock +1 -1
- data/lib/umbrellio_utils/database.rb +24 -8
- data/lib/umbrellio_utils/large_object/writer.rb +72 -0
- data/lib/umbrellio_utils/large_object.rb +61 -0
- data/lib/umbrellio_utils/version.rb +1 -1
- data/lib/umbrellio_utils.rb +1 -0
- 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: 6d29c478a9bce7022cddef6a1f54c3d11b48a23d65cc38fb385f75a751ea2022
|
|
4
|
+
data.tar.gz: 256c08e41a5860e8c430959a7e4848f36e9001f954a42c1160e6beff6758c19c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d0b45c7757a482fb326731dbc5bb778c446a1dcd55c1ccc9c4f39d000196d66c541801ce93604ad11be5fc4aba974022d190b95e3cfc2ca19dc593523a4efafb
|
|
7
|
+
data.tar.gz: '01385fd42fd555ca9ae3f3d59a2602b459ab9d2cf2d299f3ff5634caabe5e75a35e82d912287ba4c57f634bad3e99d96e3ec71f37b5ce45c062d58c948fe40c4'
|
data/Gemfile.lock
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module UmbrellioUtils
|
|
4
4
|
module Database
|
|
5
|
+
DEFAULT_ORDER = :desc
|
|
6
|
+
|
|
5
7
|
extend self
|
|
6
8
|
|
|
7
9
|
class HandledConstaintError < StandardError
|
|
@@ -28,13 +30,14 @@ module UmbrellioUtils
|
|
|
28
30
|
def each_record(dataset, primary_key: nil, **options, &block)
|
|
29
31
|
primary_key = primary_key_from(dataset, primary_key:)
|
|
30
32
|
eager_tables = Array(options.delete(:eager_load))
|
|
33
|
+
order = options.fetch(:order, DEFAULT_ORDER)
|
|
31
34
|
|
|
32
35
|
with_temp_table(dataset, primary_key:, **options) do |ids|
|
|
33
36
|
rows = ids.map { |id| row(id.is_a?(Hash) ? id.values : [id]) }
|
|
34
|
-
records =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
records = ordered(
|
|
38
|
+
dataset.model.eager(eager_tables).where(row(primary_key) => rows),
|
|
39
|
+
row(primary_key), order
|
|
40
|
+
).all
|
|
38
41
|
records.each(&block)
|
|
39
42
|
end
|
|
40
43
|
end
|
|
@@ -48,6 +51,7 @@ module UmbrellioUtils
|
|
|
48
51
|
# @option [Array] primary_key custom primary key to use for dataset
|
|
49
52
|
# @option [Symbol, String] temp_table_name custom name for temporary table,
|
|
50
53
|
# table is reused if already exists
|
|
54
|
+
# @option [Symbol] order primary key order to yield batches in, :asc or :desc
|
|
51
55
|
# rubocop:disable Metrics/ParameterLists
|
|
52
56
|
def with_temp_table(
|
|
53
57
|
dataset,
|
|
@@ -55,8 +59,12 @@ module UmbrellioUtils
|
|
|
55
59
|
sleep: nil,
|
|
56
60
|
primary_key: nil,
|
|
57
61
|
temp_table_name: nil,
|
|
58
|
-
transaction: true
|
|
62
|
+
transaction: true,
|
|
63
|
+
order: DEFAULT_ORDER
|
|
59
64
|
)
|
|
65
|
+
raise ArgumentError, "invalid order: #{order.inspect} (expected :asc or :desc)" \
|
|
66
|
+
unless %i[asc desc].include?(order)
|
|
67
|
+
|
|
60
68
|
primary_key = primary_key_from(dataset, primary_key:)
|
|
61
69
|
sleep_interval = sleep_interval_from(sleep)
|
|
62
70
|
|
|
@@ -68,7 +76,7 @@ module UmbrellioUtils
|
|
|
68
76
|
|
|
69
77
|
loop do
|
|
70
78
|
conditional_transaction(transaction) do
|
|
71
|
-
pk_set = pop_next_pk_batch(temp_table_name, primary_key, page_size)
|
|
79
|
+
pk_set = pop_next_pk_batch(temp_table_name, primary_key, page_size, order)
|
|
72
80
|
yield(pk_set) if pk_set.any?
|
|
73
81
|
end
|
|
74
82
|
|
|
@@ -122,6 +130,14 @@ module UmbrellioUtils
|
|
|
122
130
|
Sequel.function(:row, *values)
|
|
123
131
|
end
|
|
124
132
|
|
|
133
|
+
def ordered(dataset, expr, order)
|
|
134
|
+
case order
|
|
135
|
+
when :asc then dataset.order(expr)
|
|
136
|
+
when :desc then dataset.reverse(expr)
|
|
137
|
+
else raise ArgumentError, "invalid order: #{order.inspect} (expected :asc or :desc)"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
125
141
|
def extract_primary_key(dataset)
|
|
126
142
|
dataset.db.schema(dataset.first_source).select { |x| x[1][:primary_key] }.map(&:first)
|
|
127
143
|
end
|
|
@@ -147,9 +163,9 @@ module UmbrellioUtils
|
|
|
147
163
|
end
|
|
148
164
|
end
|
|
149
165
|
|
|
150
|
-
def pop_next_pk_batch(temp_table_name, primary_key, batch_size)
|
|
166
|
+
def pop_next_pk_batch(temp_table_name, primary_key, batch_size, order)
|
|
151
167
|
row = row(primary_key)
|
|
152
|
-
pk_expr = DB[temp_table_name].select(*primary_key)
|
|
168
|
+
pk_expr = ordered(DB[temp_table_name].select(*primary_key), row, order).limit(batch_size)
|
|
153
169
|
deleted_items = DB[temp_table_name].where(row => pk_expr).returning.delete
|
|
154
170
|
deleted_items.map do |item|
|
|
155
171
|
next item if primary_key.size > 1
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module UmbrellioUtils
|
|
4
|
+
class LargeObject
|
|
5
|
+
# :nocov:
|
|
6
|
+
class Writer
|
|
7
|
+
MAX_BUFFER_SIZE = PG_PAGE_SIZE * 10
|
|
8
|
+
|
|
9
|
+
attr_reader :large_object
|
|
10
|
+
|
|
11
|
+
def initialize(large_object)
|
|
12
|
+
@large_object = large_object
|
|
13
|
+
self.sio = StringIO.new.binmode
|
|
14
|
+
super()
|
|
15
|
+
|
|
16
|
+
if block_given?
|
|
17
|
+
yield(self)
|
|
18
|
+
flush
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def flush
|
|
23
|
+
sio.flush
|
|
24
|
+
large_object.append!(sio.string)
|
|
25
|
+
sio.truncate(0)
|
|
26
|
+
sio.rewind
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def tell
|
|
31
|
+
sio.tell + large_object.str_offset
|
|
32
|
+
end
|
|
33
|
+
alias pos tell
|
|
34
|
+
|
|
35
|
+
def write(*)
|
|
36
|
+
sio.write(*)
|
|
37
|
+
flush if sio.string.size >= MAX_BUFFER_SIZE
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
alias << write
|
|
41
|
+
|
|
42
|
+
def reopen(other, *)
|
|
43
|
+
sio.reopen(other.sio.string, *)
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def pos=(new_pos)
|
|
48
|
+
case
|
|
49
|
+
when new_pos < large_object.str_offset
|
|
50
|
+
flush
|
|
51
|
+
relative_pos = new_pos - large_object.str_offset
|
|
52
|
+
large_object.str_offset += relative_pos
|
|
53
|
+
when new_pos > sio.string.length
|
|
54
|
+
flush
|
|
55
|
+
large_object.str_offset = new_pos
|
|
56
|
+
else
|
|
57
|
+
sio.pos = new_pos - large_object.str_offset
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def rewind
|
|
62
|
+
large_object.str_offset = 0
|
|
63
|
+
sio.rewind
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
protected
|
|
67
|
+
|
|
68
|
+
attr_accessor :sio
|
|
69
|
+
end
|
|
70
|
+
# :nocov:
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module UmbrellioUtils
|
|
4
|
+
class LargeObject
|
|
5
|
+
FILE_END = 2
|
|
6
|
+
PG_PAGE_SIZE = 8 * 1024
|
|
7
|
+
|
|
8
|
+
class AlreadyExists < StandardError
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
autoload :Writer, "umbrellio_utils/large_object/writer"
|
|
12
|
+
|
|
13
|
+
attr_reader :oid
|
|
14
|
+
attr_accessor :str_offset
|
|
15
|
+
|
|
16
|
+
def initialize(oid = -1)
|
|
17
|
+
self.oid = oid
|
|
18
|
+
self.str_offset = 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create!
|
|
22
|
+
run(:lo_create, oid)
|
|
23
|
+
rescue Sequel::UniqueConstraintViolation
|
|
24
|
+
raise AlreadyExists.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def open_to_write!
|
|
28
|
+
DB.transaction do
|
|
29
|
+
fd = run(:lo_open, oid, 0x60000)
|
|
30
|
+
self.str_offset = run(:lo_lseek, fd, 0, FILE_END)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def append!(str)
|
|
35
|
+
run(:lo_put, oid, str_offset, Sequel.blob(str))
|
|
36
|
+
self.str_offset += str.bytesize
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def read(*)
|
|
40
|
+
run(:lo_get, oid, *)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def delete!
|
|
44
|
+
run(:lo_unlink, oid)
|
|
45
|
+
rescue PG::UndefinedObject
|
|
46
|
+
# Ignored
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def exists?
|
|
50
|
+
DB[:pg_largeobject_metadata].first(oid:).present?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
attr_writer :oid
|
|
56
|
+
|
|
57
|
+
def run(method_name, *)
|
|
58
|
+
DB.get(Sequel.function(method_name, *))
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/umbrellio_utils.rb
CHANGED
|
@@ -72,6 +72,7 @@ require_relative "umbrellio_utils/database"
|
|
|
72
72
|
require_relative "umbrellio_utils/formatting"
|
|
73
73
|
require_relative "umbrellio_utils/http_client"
|
|
74
74
|
require_relative "umbrellio_utils/jobs"
|
|
75
|
+
require_relative "umbrellio_utils/large_object" if defined?(Sequel)
|
|
75
76
|
require_relative "umbrellio_utils/migrations"
|
|
76
77
|
require_relative "umbrellio_utils/misc"
|
|
77
78
|
require_relative "umbrellio_utils/parsing"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: umbrellio-utils
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Team Umbrellio
|
|
@@ -61,6 +61,8 @@ files:
|
|
|
61
61
|
- lib/umbrellio_utils/formatting.rb
|
|
62
62
|
- lib/umbrellio_utils/http_client.rb
|
|
63
63
|
- lib/umbrellio_utils/jobs.rb
|
|
64
|
+
- lib/umbrellio_utils/large_object.rb
|
|
65
|
+
- lib/umbrellio_utils/large_object/writer.rb
|
|
64
66
|
- lib/umbrellio_utils/migrations.rb
|
|
65
67
|
- lib/umbrellio_utils/misc.rb
|
|
66
68
|
- lib/umbrellio_utils/parsing.rb
|
|
@@ -100,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
100
102
|
- !ruby/object:Gem::Version
|
|
101
103
|
version: '0'
|
|
102
104
|
requirements: []
|
|
103
|
-
rubygems_version:
|
|
105
|
+
rubygems_version: 3.6.9
|
|
104
106
|
specification_version: 4
|
|
105
107
|
summary: A set of utilities that speed up development
|
|
106
108
|
test_files: []
|