tartarus-rb 0.2.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.env.sample +4 -0
- data/.gitignore +2 -0
- data/.travis.yml +7 -0
- data/Changelog.md +18 -0
- data/Gemfile.lock +83 -4
- data/README.md +122 -6
- data/lib/tartarus.rb +2 -0
- data/lib/tartarus/archivable_collection_repository.rb +1 -0
- data/lib/tartarus/archivable_item.rb +14 -4
- data/lib/tartarus/archivable_item/sidekiq_cron_job_serializer.rb +2 -2
- data/lib/tartarus/archive_model_with_tenant.rb +8 -6
- data/lib/tartarus/archive_model_without_tenant.rb +7 -6
- data/lib/tartarus/archive_strategy.rb +3 -1
- data/lib/tartarus/archive_strategy/delete_all_using_limit_in_batches.rb +20 -0
- data/lib/tartarus/rb/version.rb +1 -1
- data/lib/tartarus/registry.rb +2 -2
- data/lib/tartarus/remote_storage.rb +4 -0
- data/lib/tartarus/remote_storage/glacier.rb +67 -0
- data/lib/tartarus/remote_storage/glacier/client.rb +26 -0
- data/lib/tartarus/remote_storage/glacier/configuration.rb +43 -0
- data/lib/tartarus/remote_storage/glacier/csv_export.rb +38 -0
- data/lib/tartarus/remote_storage/glacier/file.rb +31 -0
- data/lib/tartarus/remote_storage/glacier/register_upload.rb +28 -0
- data/lib/tartarus/remote_storage/null.rb +8 -0
- data/lib/tartarus/schedule_archiving_model.rb +4 -4
- data/lib/tartarus/sidekiq/archive_model_with_tenant_job.rb +2 -2
- data/lib/tartarus/sidekiq/archive_model_without_tenant_job.rb +2 -2
- data/lib/tartarus/sidekiq/schedule_archiving_model_job.rb +2 -2
- data/tartarus-rb.gemspec +8 -1
- metadata +97 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7669ce816882e4f9607e41afde316cbe50027291a0dd89008d3baaa9ca6b308
|
4
|
+
data.tar.gz: d03d89b04491a2dce92820674e94c895cac9cad706b790927aab9129f8c15b26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3906591489650a6e4f9ce9f3b0eaa17ab2a95797096abcf8bba9f50855a7463f0a2f1354017f3df512519775765e807ece44b4c75cbc2b3b3bd326f67a6ffcf
|
7
|
+
data.tar.gz: 48d503bc4bbea2be0196778d5dca18b9d9770cf069685128860f4567fdc1727f46042e009b43a86dc9697b217c8a6b5816a45eae1ed935f362ce63b42c2301e2
|
data/.env.sample
ADDED
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -3,6 +3,13 @@ language: ruby
|
|
3
3
|
cache: bundler
|
4
4
|
rvm:
|
5
5
|
- 2.7.2
|
6
|
+
env:
|
7
|
+
global:
|
8
|
+
- AWS_KEY=AWS_KEY
|
9
|
+
- AWS_SECRET=AWS_SECRET
|
10
|
+
- AWS_REGION="us-east-2"
|
11
|
+
- VAULT_NAME=VAULT_NAME
|
6
12
|
before_install: gem install bundler -v 2.1.4
|
7
13
|
services:
|
8
14
|
- redis-server
|
15
|
+
- postgresql
|
data/Changelog.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## Master
|
4
|
+
|
5
|
+
## 0.5.0
|
6
|
+
|
7
|
+
- Provide ability to explicitly set the name of archivable item to have multiple ways of archiving the same model
|
8
|
+
|
9
|
+
## 0.4.1
|
10
|
+
|
11
|
+
- Do not make Glacier a required dependency if not used
|
12
|
+
|
13
|
+
## 0.4.0
|
14
|
+
|
15
|
+
- Add Glacier remote storage support to upload data before deleting it
|
16
|
+
|
17
|
+
## 0.3.0
|
18
|
+
|
19
|
+
- Add `delete_all_using_limit_in_batches` strategy
|
20
|
+
|
3
21
|
## 0.2.0
|
4
22
|
- Add support for deleting and destroying in batches
|
5
23
|
- Add integration tests with a real database and ActiveRecord
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,26 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
tartarus-rb (0.
|
4
|
+
tartarus-rb (0.5.0)
|
5
5
|
sidekiq (>= 5)
|
6
6
|
sidekiq-cron (~> 1)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
+
actionpack (6.1.0.rc1)
|
12
|
+
actionview (= 6.1.0.rc1)
|
13
|
+
activesupport (= 6.1.0.rc1)
|
14
|
+
rack (~> 2.0, >= 2.0.9)
|
15
|
+
rack-test (>= 0.6.3)
|
16
|
+
rails-dom-testing (~> 2.0)
|
17
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
18
|
+
actionview (6.1.0.rc1)
|
19
|
+
activesupport (= 6.1.0.rc1)
|
20
|
+
builder (~> 3.1)
|
21
|
+
erubi (~> 1.4)
|
22
|
+
rails-dom-testing (~> 2.0)
|
23
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
11
24
|
activemodel (6.1.0.rc1)
|
12
25
|
activesupport (= 6.1.0.rc1)
|
13
26
|
activerecord (6.1.0.rc1)
|
@@ -19,21 +32,74 @@ GEM
|
|
19
32
|
minitest (>= 5.1)
|
20
33
|
tzinfo (~> 2.0)
|
21
34
|
zeitwerk (~> 2.3)
|
35
|
+
addressable (2.7.0)
|
36
|
+
public_suffix (>= 2.0.2, < 5.0)
|
37
|
+
aws-eventstream (1.1.0)
|
38
|
+
aws-partitions (1.424.0)
|
39
|
+
aws-sdk-core (3.112.0)
|
40
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
41
|
+
aws-partitions (~> 1, >= 1.239.0)
|
42
|
+
aws-sigv4 (~> 1.1)
|
43
|
+
jmespath (~> 1.0)
|
44
|
+
aws-sdk-glacier (1.36.0)
|
45
|
+
aws-sdk-core (~> 3, >= 3.112.0)
|
46
|
+
aws-sigv4 (~> 1.1)
|
47
|
+
aws-sigv4 (1.2.2)
|
48
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
49
|
+
builder (3.2.4)
|
22
50
|
concurrent-ruby (1.1.7)
|
23
51
|
connection_pool (2.2.3)
|
52
|
+
crack (0.4.3)
|
53
|
+
safe_yaml (~> 1.0.0)
|
54
|
+
crass (1.0.6)
|
24
55
|
diff-lcs (1.4.4)
|
56
|
+
dotenv (2.7.6)
|
57
|
+
erubi (1.10.0)
|
25
58
|
et-orbi (1.2.4)
|
26
59
|
tzinfo
|
27
|
-
fugit (1.4.
|
60
|
+
fugit (1.4.5)
|
28
61
|
et-orbi (~> 1.1, >= 1.1.8)
|
29
62
|
raabro (~> 1.4)
|
63
|
+
hashdiff (1.0.0)
|
30
64
|
i18n (1.8.5)
|
31
65
|
concurrent-ruby (~> 1.0)
|
66
|
+
jmespath (1.4.0)
|
67
|
+
loofah (2.9.0)
|
68
|
+
crass (~> 1.0.2)
|
69
|
+
nokogiri (>= 1.5.9)
|
70
|
+
method_source (1.0.0)
|
71
|
+
mini_portile2 (2.5.0)
|
32
72
|
minitest (5.14.2)
|
73
|
+
nokogiri (1.11.1)
|
74
|
+
mini_portile2 (~> 2.5.0)
|
75
|
+
racc (~> 1.4)
|
76
|
+
pg (1.2.3)
|
77
|
+
postgres-copy (1.5.0)
|
78
|
+
activerecord (>= 5.1)
|
79
|
+
pg (>= 0.17)
|
80
|
+
responders
|
81
|
+
public_suffix (4.0.4)
|
33
82
|
raabro (1.4.0)
|
83
|
+
racc (1.5.2)
|
34
84
|
rack (2.2.3)
|
85
|
+
rack-test (1.1.0)
|
86
|
+
rack (>= 1.0, < 3)
|
87
|
+
rails-dom-testing (2.0.3)
|
88
|
+
activesupport (>= 4.2.0)
|
89
|
+
nokogiri (>= 1.6)
|
90
|
+
rails-html-sanitizer (1.3.0)
|
91
|
+
loofah (~> 2.3)
|
92
|
+
railties (6.1.0.rc1)
|
93
|
+
actionpack (= 6.1.0.rc1)
|
94
|
+
activesupport (= 6.1.0.rc1)
|
95
|
+
method_source
|
96
|
+
rake (>= 0.8.7)
|
97
|
+
thor (~> 1.0)
|
35
98
|
rake (13.0.1)
|
36
99
|
redis (4.2.2)
|
100
|
+
responders (3.0.1)
|
101
|
+
actionpack (>= 5.0)
|
102
|
+
railties (>= 5.0)
|
37
103
|
rspec (3.9.0)
|
38
104
|
rspec-core (~> 3.9.0)
|
39
105
|
rspec-expectations (~> 3.9.0)
|
@@ -50,6 +116,7 @@ GEM
|
|
50
116
|
rspec-core (~> 3.0, >= 3.0.0)
|
51
117
|
sidekiq (>= 2.4.0)
|
52
118
|
rspec-support (3.9.3)
|
119
|
+
safe_yaml (1.0.5)
|
53
120
|
sidekiq (6.1.2)
|
54
121
|
connection_pool (>= 2.2.2)
|
55
122
|
rack (~> 2.0)
|
@@ -57,9 +124,15 @@ GEM
|
|
57
124
|
sidekiq-cron (1.2.0)
|
58
125
|
fugit (~> 1.1)
|
59
126
|
sidekiq (>= 4.2.1)
|
60
|
-
|
127
|
+
thor (1.1.0)
|
128
|
+
timecop (0.9.2)
|
61
129
|
tzinfo (2.0.2)
|
62
130
|
concurrent-ruby (~> 1.0)
|
131
|
+
vcr (6.0.0)
|
132
|
+
webmock (3.7.6)
|
133
|
+
addressable (>= 2.3.6)
|
134
|
+
crack (>= 0.3.2)
|
135
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
63
136
|
zeitwerk (2.4.1)
|
64
137
|
|
65
138
|
PLATFORMS
|
@@ -67,11 +140,17 @@ PLATFORMS
|
|
67
140
|
|
68
141
|
DEPENDENCIES
|
69
142
|
activerecord (~> 6)
|
143
|
+
aws-sdk-glacier
|
144
|
+
dotenv
|
145
|
+
pg
|
146
|
+
postgres-copy
|
70
147
|
rake (~> 13.0)
|
71
148
|
rspec (~> 3.0)
|
72
149
|
rspec-sidekiq
|
73
|
-
sqlite3
|
74
150
|
tartarus-rb!
|
151
|
+
timecop
|
152
|
+
vcr
|
153
|
+
webmock
|
75
154
|
|
76
155
|
BUNDLED WITH
|
77
156
|
2.1.4
|
data/README.md
CHANGED
@@ -56,12 +56,23 @@ if File.exist?(schedule_file) && Sidekiq.server?
|
|
56
56
|
item.timestamp_field = :created_at
|
57
57
|
end
|
58
58
|
|
59
|
+
glacier_configuration = Tartarus::RemoteStorage::Glacier::Configuration.build(
|
60
|
+
aws_key: ENV.fetch("AWS_KEY"),
|
61
|
+
aws_secret: ENV.fetch("AWS_SECRET"),
|
62
|
+
aws_region: ENV.fetch("AWS_REGION"),
|
63
|
+
vault_name: ENV.fetch("GLACIER_VAULT_NAME"),
|
64
|
+
root_path: Rails.root.to_s,
|
65
|
+
archive_registry_factory: ArchiveRegistry,
|
66
|
+
)
|
67
|
+
# don't forget about installing `aws-sdk-glacier` gem
|
68
|
+
|
59
69
|
tartarus.register do |item|
|
60
70
|
item.model = YetAnotherModel
|
61
71
|
item.cron = "5 6 * * *"
|
62
72
|
item.queue = "default"
|
63
73
|
item.timestamp_field = :created_at
|
64
74
|
item.archive_items_older_than = -> { 1.week.ago }
|
75
|
+
item.remote_storage = Tartarus::RemoteStorage::Glacier.new(glacier_configuration)
|
65
76
|
end
|
66
77
|
|
67
78
|
tartarus.schedule # this method must be called to create jobs for sidekiq-cron!
|
@@ -71,6 +82,31 @@ end
|
|
71
82
|
|
72
83
|
You can use the following config params:
|
73
84
|
- `model` - a name of the ActiveReord model you want to archive, required
|
85
|
+
- `name` - name of your strategy, optional. It fallbacks `model.to_s`. It's important to set in in cases when you have several strategies for the same model:
|
86
|
+
```rb
|
87
|
+
tartarus.register do |item|
|
88
|
+
item.model = InternalEvent
|
89
|
+
item.name = "archive_account_and_user_internal_events"
|
90
|
+
item.cron = "5 5 * * *"
|
91
|
+
item.queue = "default"
|
92
|
+
item.tenants_range = -> { ["Account", "User"] }
|
93
|
+
item.tenant_id_field = :model_type
|
94
|
+
item.archive_items_older_than = -> { 30.days.ago }
|
95
|
+
item.timestamp_field = :created_at
|
96
|
+
end
|
97
|
+
|
98
|
+
tartarus.register do |item|
|
99
|
+
item.model = InternalEvent
|
100
|
+
item.name = "archive_post_and_comment_internal_events"
|
101
|
+
item.cron = "5 15 * * *"
|
102
|
+
item.queue = "default"
|
103
|
+
item.tenants_range = -> { ["Post", "Comment"] }
|
104
|
+
item.tenant_id_field = :model_type
|
105
|
+
item.archive_items_older_than = -> { 10.days.ago }
|
106
|
+
item.timestamp_field = :created_at
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
74
110
|
- `cron` - cron syntax, required
|
75
111
|
- `queue` - name of the sidekiq queue you want to use for execution of the jobs, required
|
76
112
|
- `tenants_range` - optional, use if you want to scope items by a tenant (or any field that can be used for partitioning). It doesn't have to be ActiveRecord collection, could be just an array. Must be a proc/lambda/object responding to `call` method. For ActvieRecord collection, `find_each` loop will be used for optimization.
|
@@ -78,7 +114,92 @@ You can use the following config params:
|
|
78
114
|
- `tenant_id_field` - required when using tenant_value_source/tenant_value_source. It's a DB column that will be used for scoping records by a tenant. For example, here it would be: `ModelThatYouWantToArchive.where(account_uuid: value_of_uuid_from_some_active_account)`
|
79
115
|
- `archive_items_older_than` - required, for defining retention policy
|
80
116
|
- `timestamp_field` - required, used for performing a query using the value from `archive_items_older_than`
|
81
|
-
- `archive_with` - optional (defaults to `delete_all`). Could be `delete_all`, `destroy_all`, `delete_all_without_batches`, `destroy_all_without_batches`
|
117
|
+
- `archive_with` - optional (defaults to `delete_all`). Could be `delete_all`, `destroy_all`, `delete_all_without_batches`, `destroy_all_without_batches`, `delete_all_using_limit_in_batches`
|
118
|
+
- `batch_size` - optional (defaults to `10_000`, used with `delete_all_using_limit_in_batches` strategy)
|
119
|
+
- `remote_storage` - optional (defaults to `Tartarus::RemoteStorage::Null` which does nothing). Use this option if you want store the data somewhere before deleting it.
|
120
|
+
|
121
|
+
### Remote Storage
|
122
|
+
|
123
|
+
Currently, only `Glacier` (for AWS Glacier) is supported. Also, it works only with Postgres database and requires [postgres-copy](https://github.com/diogob/postgres-copy).
|
124
|
+
|
125
|
+
To take advantage of this feature you will need a couple of things:
|
126
|
+
1. Apply `acts_as_copy_target` to the archivable model (from `postgres-copy` gem).
|
127
|
+
2. Create a model that will be used as a registry for all uploads that happened.
|
128
|
+
3. Install `aws-sdk-glacier` gem.
|
129
|
+
|
130
|
+
If you want to make `Version` model archivable and use `ArchiveRegistry` as the registry, you will need the following models and tables:
|
131
|
+
|
132
|
+
``` rb
|
133
|
+
database.create_table(:archive_registries) do |t|
|
134
|
+
t.string :glacier_location, null: false
|
135
|
+
t.string :glacier_checksum, null: false
|
136
|
+
t.string :glacier_archive_id, null: false
|
137
|
+
t.string :archivable_model, null: false
|
138
|
+
t.string :tenant_id_field
|
139
|
+
t.string :tenant_id
|
140
|
+
t.datetime :completed_at, null: false
|
141
|
+
end
|
142
|
+
|
143
|
+
database.create_table(:versions) do |t|
|
144
|
+
end
|
145
|
+
|
146
|
+
class Version < ApplicationRecord
|
147
|
+
acts_as_copy_target
|
148
|
+
end
|
149
|
+
|
150
|
+
class ArchiveRegistry < ApplicationRecord
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
You can use the above schema for the registry model as it contains all needed fields.
|
155
|
+
|
156
|
+
To initialize the service:
|
157
|
+
|
158
|
+
``` rb
|
159
|
+
glacier_configuration = Tartarus::RemoteStorage::Glacier::Configuration.build(
|
160
|
+
aws_key: ENV.fetch("AWS_KEY"),
|
161
|
+
aws_secret: ENV.fetch("AWS_SECRET"),
|
162
|
+
aws_region: ENV.fetch("AWS_REGION"),
|
163
|
+
vault_name: ENV.fetch("GLACIER_VAULT_NAME"),
|
164
|
+
root_path: Rails.root.to_s,
|
165
|
+
archive_registry_factory: ArchiveRegistry,
|
166
|
+
)
|
167
|
+
Tartarus::RemoteStorage::Glacier.new(glacier_configuration)
|
168
|
+
```
|
169
|
+
|
170
|
+
You can also pass `account_id` (by default "-" string will be used):
|
171
|
+
|
172
|
+
``` rb
|
173
|
+
glacier_configuration = Tartarus::RemoteStorage::Glacier::Configuration.build(
|
174
|
+
aws_key: ENV.fetch("AWS_KEY"),
|
175
|
+
aws_secret: ENV.fetch("AWS_SECRET"),
|
176
|
+
aws_region: ENV.fetch("AWS_REGION"),
|
177
|
+
vault_name: ENV.fetch("GLACIER_VAULT_NAME"),
|
178
|
+
root_path: Rails.root.to_s,
|
179
|
+
archive_registry_factory: ArchiveRegistry,
|
180
|
+
account_id: "some_account_id"
|
181
|
+
)
|
182
|
+
Tartarus::RemoteStorage::Glacier.new(glacier_configuration)
|
183
|
+
```
|
184
|
+
|
185
|
+
**Important** - do not use Glacier Storage for large batches (> 4 GB) as multipart uploads are not supported yet.
|
186
|
+
|
187
|
+
|
188
|
+
If you know what you are doing, you can add your own storage, as long as it complies with the following interface:
|
189
|
+
|
190
|
+
``` rb
|
191
|
+
class Glacier
|
192
|
+
attr_reader :configuration
|
193
|
+
private :configuration
|
194
|
+
|
195
|
+
def initialize(configuration)
|
196
|
+
@configuration = configuration
|
197
|
+
end
|
198
|
+
|
199
|
+
def store(collection, archivable_model, tenant_id: nil, tenant_id_field: nil)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
```
|
82
203
|
|
83
204
|
### Testing before actually using it
|
84
205
|
|
@@ -87,7 +208,6 @@ You might want to verify that the gem works in the way you expect it to work. Fo
|
|
87
208
|
1. scheduling/enqueueing: use `Tartarus::ScheduleArchivingModel#schedule` - for example, `Tartarus::ScheduleArchivingModel.new.schedule("PaperTrailVersion")`, it's going to enqueue either `Tartarus::Sidekiq::ArchiveModelWithTenantJob` or `Tartarus::Sidekiq::ArchiveModelWithoutTenantJob`, depending on the config.
|
88
209
|
2. execution of the archiving logic: use `Tartarus::ArchiveModelWithTenant#archive` (for example, `Tartarus::ArchiveModelWithTenant.new.archive("PaperTrailVersion", "User")`) or `Tartarus::ArchiveModelWithoutTenant#archive` (for example, `Tartarus::ArchiveModelWithoutTenant.new.archive("PaperTrailVersion")`)
|
89
210
|
|
90
|
-
|
91
211
|
You might also want to check `spec/integration` to get an idea how the integration tests were written.
|
92
212
|
|
93
213
|
## Development
|
@@ -96,10 +216,6 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
96
216
|
|
97
217
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
98
218
|
|
99
|
-
## TODO
|
100
|
-
|
101
|
-
- add support for uploading archives to AWS Glacier before deleting items
|
102
|
-
|
103
219
|
## Contributing
|
104
220
|
|
105
221
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/tartarus-rb.
|
data/lib/tartarus.rb
CHANGED
@@ -4,6 +4,7 @@ require "tartarus/archivable_item/sidekiq_cron_job_serializer"
|
|
4
4
|
require "tartarus/archive_strategy"
|
5
5
|
require "tartarus/archive_strategy/delete_all"
|
6
6
|
require "tartarus/archive_strategy/delete_all_without_batches"
|
7
|
+
require "tartarus/archive_strategy/delete_all_using_limit_in_batches"
|
7
8
|
require "tartarus/archive_strategy/destroy_all"
|
8
9
|
require "tartarus/archive_strategy/destroy_all_without_batches"
|
9
10
|
require "tartarus/archive_strategy/extract_batch"
|
@@ -17,6 +18,7 @@ require "tartarus/rb/version"
|
|
17
18
|
require "tartarus/registry"
|
18
19
|
require "tartarus/repository"
|
19
20
|
require "tartarus/schedule_archiving_model"
|
21
|
+
require "tartarus/remote_storage"
|
20
22
|
require "sidekiq/cron/job"
|
21
23
|
require "sidekiq"
|
22
24
|
|
@@ -13,6 +13,7 @@ class Tartarus
|
|
13
13
|
ensure_column_exists(collection, model_name, tenant_id_field)
|
14
14
|
|
15
15
|
collection.where("#{timestamp_field} < ?", timestamp).where(tenant_id_field => tenant_id)
|
16
|
+
.order(tenant_id_field, timestamp_field)
|
16
17
|
end
|
17
18
|
|
18
19
|
def items_older_than(model_name, timestamp_field, timestamp)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Tartarus::ArchivableItem
|
2
2
|
REQUIRED_ATTRIBUTES_NAMES = %i(model cron queue archive_items_older_than timestamp_field active_job
|
3
3
|
archive_with tenant_value_source).freeze
|
4
|
-
OPTIONAL_ATTRIBUTES_NAMES = %i(tenants_range tenant_id_field).freeze
|
4
|
+
OPTIONAL_ATTRIBUTES_NAMES = %i(tenants_range tenant_id_field batch_size remote_storage name).freeze
|
5
5
|
|
6
6
|
attr_accessor *(REQUIRED_ATTRIBUTES_NAMES + OPTIONAL_ATTRIBUTES_NAMES)
|
7
7
|
|
@@ -45,6 +45,16 @@ class Tartarus::ArchivableItem
|
|
45
45
|
@archive_with ||= :delete_all
|
46
46
|
end
|
47
47
|
|
48
|
+
def batch_size
|
49
|
+
return @batch_size if defined?(@batch_size)
|
50
|
+
|
51
|
+
@batch_size ||= 10_000
|
52
|
+
end
|
53
|
+
|
54
|
+
def name
|
55
|
+
@name || @model.to_s
|
56
|
+
end
|
57
|
+
|
48
58
|
def validate!
|
49
59
|
validate_presence
|
50
60
|
end
|
@@ -54,11 +64,11 @@ class Tartarus::ArchivableItem
|
|
54
64
|
end
|
55
65
|
|
56
66
|
def archive_strategy(factory: Tartarus::ArchiveStrategy.new)
|
57
|
-
factory.for(archive_with)
|
67
|
+
factory.for(archive_with, batch_size: batch_size)
|
58
68
|
end
|
59
69
|
|
60
|
-
def
|
61
|
-
|
70
|
+
def remote_storage
|
71
|
+
@remote_storage || Tartarus::RemoteStorage::Null
|
62
72
|
end
|
63
73
|
|
64
74
|
private
|
@@ -7,7 +7,7 @@ class Tartarus
|
|
7
7
|
description: description_for_item(archivable_item),
|
8
8
|
cron: archivable_item.cron,
|
9
9
|
class: Tartarus::Sidekiq::ScheduleArchivingModelJob,
|
10
|
-
args: [archivable_item.
|
10
|
+
args: [archivable_item.name],
|
11
11
|
queue: archivable_item.queue,
|
12
12
|
active_job: archivable_item.active_job
|
13
13
|
}
|
@@ -16,7 +16,7 @@ class Tartarus
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def name_for_item(archivable_item)
|
19
|
-
"TARTARUS_#{archivable_item.
|
19
|
+
"TARTARUS_#{archivable_item.name}"
|
20
20
|
end
|
21
21
|
|
22
22
|
def description_for_item(archivable_item)
|
@@ -7,18 +7,20 @@ class Tartarus::ArchiveModelWithTenant
|
|
7
7
|
@repository = repository
|
8
8
|
end
|
9
9
|
|
10
|
-
def archive(
|
11
|
-
archivable_item = registry.
|
12
|
-
|
13
|
-
archivable_item.
|
10
|
+
def archive(archivable_item_name, tenant_id)
|
11
|
+
archivable_item = registry.find_by_name(archivable_item_name)
|
12
|
+
collection = collection_to_archive(archivable_item, tenant_id)
|
13
|
+
archivable_item.remote_storage.store(collection, archivable_item.name, tenant_id: tenant_id,
|
14
|
+
tenant_id_field: archivable_item.tenant_id_field)
|
15
|
+
archivable_item.archive_strategy.call(collection)
|
14
16
|
end
|
15
17
|
|
16
18
|
private
|
17
19
|
|
18
|
-
def collection_to_archive(
|
20
|
+
def collection_to_archive(archivable_item, tenant_id)
|
19
21
|
repository
|
20
22
|
.items_older_than_for_tenant(
|
21
|
-
|
23
|
+
archivable_item.model,
|
22
24
|
archivable_item.timestamp_field, archivable_item.archive_items_older_than.call,
|
23
25
|
archivable_item.tenant_id_field, tenant_id
|
24
26
|
)
|
@@ -7,18 +7,19 @@ class Tartarus::ArchiveModelWithoutTenant
|
|
7
7
|
@repository = repository
|
8
8
|
end
|
9
9
|
|
10
|
-
def archive(
|
11
|
-
archivable_item = registry.
|
12
|
-
|
13
|
-
archivable_item.
|
10
|
+
def archive(archivable_item_name)
|
11
|
+
archivable_item = registry.find_by_name(archivable_item_name)
|
12
|
+
collection = collection_to_archive(archivable_item)
|
13
|
+
archivable_item.remote_storage.store(collection, archivable_item.name)
|
14
|
+
archivable_item.archive_strategy.call(collection)
|
14
15
|
end
|
15
16
|
|
16
17
|
private
|
17
18
|
|
18
|
-
def collection_to_archive(
|
19
|
+
def collection_to_archive(archivable_item)
|
19
20
|
repository
|
20
21
|
.items_older_than(
|
21
|
-
|
22
|
+
archivable_item.model,
|
22
23
|
archivable_item.timestamp_field, archivable_item.archive_items_older_than.call
|
23
24
|
)
|
24
25
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Tartarus::ArchiveStrategy
|
2
|
-
def for(strategy_name)
|
2
|
+
def for(strategy_name, batch_size: 0)
|
3
3
|
case strategy_name.to_sym
|
4
4
|
when :delete_all
|
5
5
|
Tartarus::ArchiveStrategy::DeleteAll.new
|
@@ -9,6 +9,8 @@ class Tartarus::ArchiveStrategy
|
|
9
9
|
Tartarus::ArchiveStrategy::DeleteAllWithoutBatches.new
|
10
10
|
when :destroy_all_without_batches
|
11
11
|
Tartarus::ArchiveStrategy::DestroyAllWithoutBatches.new
|
12
|
+
when :delete_all_using_limit_in_batches
|
13
|
+
Tartarus::ArchiveStrategy::DeleteAllUsingLimitInBatches.new(batch_size: batch_size)
|
12
14
|
else
|
13
15
|
raise "unknown strategy: #{strategy_name}"
|
14
16
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Tartarus
|
2
|
+
class ArchiveStrategy
|
3
|
+
class DeleteAllUsingLimitInBatches
|
4
|
+
attr_reader :batch_size
|
5
|
+
private :batch_size
|
6
|
+
|
7
|
+
def initialize(batch_size:)
|
8
|
+
@batch_size = batch_size
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(collection)
|
12
|
+
num = 1
|
13
|
+
|
14
|
+
while num > 0
|
15
|
+
num = collection.limit(batch_size).delete_all
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/tartarus/rb/version.rb
CHANGED
data/lib/tartarus/registry.rb
CHANGED
@@ -16,8 +16,8 @@ class Tartarus::Registry
|
|
16
16
|
@storage << item
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
storage.find(->{ raise "#{
|
19
|
+
def find_by_name(name)
|
20
|
+
storage.find(->{ raise "#{name} not found in registry" }) { |item| item.name == name }
|
21
21
|
end
|
22
22
|
|
23
23
|
def reset
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "aws-sdk-glacier"
|
2
|
+
require "tartarus/remote_storage/glacier/client"
|
3
|
+
require "tartarus/remote_storage/glacier/file"
|
4
|
+
require "tartarus/remote_storage/glacier/csv_export"
|
5
|
+
require "tartarus/remote_storage/glacier/register_upload"
|
6
|
+
require "tartarus/remote_storage/glacier/configuration"
|
7
|
+
|
8
|
+
class Tartarus
|
9
|
+
module RemoteStorage
|
10
|
+
class Glacier
|
11
|
+
attr_reader :configuration, :clock
|
12
|
+
private :configuration, :clock
|
13
|
+
|
14
|
+
def initialize(configuration, clock: Time)
|
15
|
+
@configuration = configuration
|
16
|
+
@clock = clock
|
17
|
+
end
|
18
|
+
|
19
|
+
def store(collection, archivable_model, tenant_id: nil, tenant_id_field: nil)
|
20
|
+
path_to_file = path_to_file_for(archivable_model, tenant_id_field, tenant_id)
|
21
|
+
export_to_csv(collection, path_to_file)
|
22
|
+
glacier_file = Tartarus::RemoteStorage::Glacier::File.new(::File.new(path_to_file))
|
23
|
+
glacier_response = upload(glacier_file)
|
24
|
+
register_upload(glacier_response, archivable_model, tenant_id_field, tenant_id)
|
25
|
+
ensure
|
26
|
+
glacier_file.delete_from_local_storage if glacier_file
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def upload(file)
|
32
|
+
client.upload_archive(configuration.vault_name, file)
|
33
|
+
end
|
34
|
+
|
35
|
+
def client
|
36
|
+
@client ||= begin
|
37
|
+
Tartarus::RemoteStorage::Glacier::Client.new(
|
38
|
+
key: configuration.aws_key,
|
39
|
+
secret: configuration.aws_secret,
|
40
|
+
region: configuration.aws_region,
|
41
|
+
account_id: configuration.account_id,
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def export_to_csv(collection, path_to_file)
|
47
|
+
Tartarus::RemoteStorage::Glacier::CsvExport
|
48
|
+
.new(configuration.storage_directory)
|
49
|
+
.export(collection, path_to_file)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def register_upload(glacier_response, archivable_model, tenant_id_field, tenant_id)
|
54
|
+
Tartarus::RemoteStorage::Glacier::RegisterUpload.new(configuration.archive_registry_factory).register(
|
55
|
+
glacier_response,
|
56
|
+
archivable_model,
|
57
|
+
tenant_id_field,
|
58
|
+
tenant_id
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def path_to_file_for(archivable_model, tenant_id_field, tenant_id)
|
63
|
+
"#{configuration.storage_directory}/#{archivable_model}_#{tenant_id_field}_#{tenant_id}_#{clock.now.to_i}.csv"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Tartarus
|
4
|
+
module RemoteStorage
|
5
|
+
class Glacier
|
6
|
+
class Client
|
7
|
+
attr_reader :client, :account_id
|
8
|
+
private :client, :account_id
|
9
|
+
|
10
|
+
def initialize(key:, secret:, region:, account_id:)
|
11
|
+
@client = Aws::Glacier::Client.new(credentials: Aws::Credentials.new(key, secret), region: region)
|
12
|
+
@account_id = account_id
|
13
|
+
end
|
14
|
+
|
15
|
+
def upload_archive(vault_name, file)
|
16
|
+
client.upload_archive(
|
17
|
+
account_id: account_id,
|
18
|
+
archive_description: file.description,
|
19
|
+
body: file.body,
|
20
|
+
vault_name: vault_name
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Tartarus
|
2
|
+
module RemoteStorage
|
3
|
+
class Glacier
|
4
|
+
class Configuration
|
5
|
+
DEFAULT_ACCOUNT_ID = "-"
|
6
|
+
private_constant :DEFAULT_ACCOUNT_ID
|
7
|
+
|
8
|
+
REQUIRED_ATTRIBUTES_NAMES = %i(aws_key aws_secret aws_region account_id vault_name root_path
|
9
|
+
archive_registry_factory).freeze
|
10
|
+
attr_accessor *REQUIRED_ATTRIBUTES_NAMES
|
11
|
+
|
12
|
+
def self.build(aws_key:, aws_secret:, aws_region:, account_id: DEFAULT_ACCOUNT_ID, vault_name:, root_path:, archive_registry_factory:)
|
13
|
+
new.tap do |config|
|
14
|
+
config.aws_key = aws_key
|
15
|
+
config.aws_secret = aws_secret
|
16
|
+
config.aws_region = aws_region
|
17
|
+
config.account_id = account_id
|
18
|
+
config.vault_name = vault_name
|
19
|
+
config.root_path = root_path
|
20
|
+
config.archive_registry_factory = archive_registry_factory
|
21
|
+
config.validate!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate!
|
26
|
+
validate_presence
|
27
|
+
end
|
28
|
+
|
29
|
+
def storage_directory
|
30
|
+
"#{root_path}/tmp/tartarus/#{archive_registry_factory}"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def validate_presence
|
36
|
+
REQUIRED_ATTRIBUTES_NAMES.each do |attribute|
|
37
|
+
raise ":#{attribute} must be present" if public_send(attribute).nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Tartarus
|
2
|
+
module RemoteStorage
|
3
|
+
class Glacier
|
4
|
+
class CsvExport
|
5
|
+
FILE_MODE = "w"
|
6
|
+
DELIMITER = ";"
|
7
|
+
NO_PATH_FOR_EXPORT = nil
|
8
|
+
ENCODING = "UTF-8"
|
9
|
+
private_constant :FILE_MODE, :DELIMITER, :NO_PATH_FOR_EXPORT, :ENCODING
|
10
|
+
|
11
|
+
attr_reader :storage_directory, :file_service, :file_utils
|
12
|
+
private :storage_directory, :file_service, :file_utils
|
13
|
+
|
14
|
+
def initialize(storage_directory, file_service: ::File, file_utils: FileUtils)
|
15
|
+
@storage_directory = storage_directory
|
16
|
+
@file_service = file_service
|
17
|
+
@file_utils = file_utils
|
18
|
+
end
|
19
|
+
|
20
|
+
def export(collection, path_to_file)
|
21
|
+
with_csv_export_file(path_to_file) do |file|
|
22
|
+
collection.copy_to(NO_PATH_FOR_EXPORT, delimiter: DELIMITER) do |line|
|
23
|
+
file.write(line.force_encoding(ENCODING))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def with_csv_export_file(path_to_file, &block)
|
31
|
+
file_utils.mkdir_p(storage_directory) if !file_service.exist?(storage_directory)
|
32
|
+
|
33
|
+
file_service.open(path_to_file, FILE_MODE, &block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
class Tartarus
|
4
|
+
module RemoteStorage
|
5
|
+
class Glacier
|
6
|
+
class File < SimpleDelegator
|
7
|
+
def description
|
8
|
+
file_service.basename(self, ".*")
|
9
|
+
end
|
10
|
+
|
11
|
+
def body
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def checksum
|
16
|
+
Digest::SHA256.file(path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete_from_local_storage
|
20
|
+
file_service.delete(path)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def file_service
|
26
|
+
::File
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Tartarus
|
2
|
+
module RemoteStorage
|
3
|
+
class Glacier
|
4
|
+
class RegisterUpload
|
5
|
+
attr_reader :archive_registry_factory, :clock
|
6
|
+
private :archive_registry_factory, :clock
|
7
|
+
|
8
|
+
def initialize(archive_registry_factory, clock: Time)
|
9
|
+
@archive_registry_factory = archive_registry_factory
|
10
|
+
@clock = clock
|
11
|
+
end
|
12
|
+
|
13
|
+
def register(glacier_response, archivable_model, tenant_id_field, tenant_id)
|
14
|
+
archive_registry_factory.new.tap do |archive_registry|
|
15
|
+
archive_registry.glacier_location = glacier_response.location
|
16
|
+
archive_registry.glacier_checksum = glacier_response.checksum
|
17
|
+
archive_registry.glacier_archive_id = glacier_response.archive_id
|
18
|
+
archive_registry.archivable_model = archivable_model
|
19
|
+
archive_registry.tenant_id_field = tenant_id_field
|
20
|
+
archive_registry.tenant_id = tenant_id
|
21
|
+
archive_registry.completed_at = clock.now
|
22
|
+
archive_registry.save!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -6,15 +6,15 @@ class Tartarus::ScheduleArchivingModel
|
|
6
6
|
@registry = registry
|
7
7
|
end
|
8
8
|
|
9
|
-
def schedule(
|
10
|
-
archivable_item = registry.
|
9
|
+
def schedule(archivable_item_name)
|
10
|
+
archivable_item = registry.find_by_name(archivable_item_name)
|
11
11
|
|
12
12
|
if archivable_item.scope_by_tenant?
|
13
13
|
each_tenant(archivable_item) do |tenant|
|
14
|
-
enqueue(Tartarus::Sidekiq::ArchiveModelWithTenantJob, archivable_item.queue,
|
14
|
+
enqueue(Tartarus::Sidekiq::ArchiveModelWithTenantJob, archivable_item.queue, archivable_item.name, tenant)
|
15
15
|
end
|
16
16
|
else
|
17
|
-
enqueue(Tartarus::Sidekiq::ArchiveModelWithoutTenantJob, archivable_item.queue,
|
17
|
+
enqueue(Tartarus::Sidekiq::ArchiveModelWithoutTenantJob, archivable_item.queue, archivable_item.name)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -4,8 +4,8 @@ class Tartarus
|
|
4
4
|
class Sidekiq::ArchiveModelWithTenantJob
|
5
5
|
include ::Sidekiq::Worker
|
6
6
|
|
7
|
-
def perform(
|
8
|
-
Tartarus::ArchiveModelWithTenant.new.archive(
|
7
|
+
def perform(archivable_item_name, tenant_id)
|
8
|
+
Tartarus::ArchiveModelWithTenant.new.archive(archivable_item_name, tenant_id)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -4,8 +4,8 @@ class Tartarus
|
|
4
4
|
class Sidekiq::ArchiveModelWithoutTenantJob
|
5
5
|
include ::Sidekiq::Worker
|
6
6
|
|
7
|
-
def perform(
|
8
|
-
Tartarus::ArchiveModelWithoutTenant.new.archive(
|
7
|
+
def perform(archivable_item_name)
|
8
|
+
Tartarus::ArchiveModelWithoutTenant.new.archive(archivable_item_name)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -4,8 +4,8 @@ class Tartarus
|
|
4
4
|
class Sidekiq::ScheduleArchivingModelJob
|
5
5
|
include ::Sidekiq::Worker
|
6
6
|
|
7
|
-
def perform(
|
8
|
-
Tartarus::ScheduleArchivingModel.new.schedule(
|
7
|
+
def perform(archivable_item_name)
|
8
|
+
Tartarus::ScheduleArchivingModel.new.schedule(archivable_item_name)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
data/tartarus-rb.gemspec
CHANGED
@@ -30,10 +30,17 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_dependency "sidekiq", ">= 5"
|
31
31
|
spec.add_dependency "sidekiq-cron", "~> 1"
|
32
32
|
|
33
|
+
|
33
34
|
spec.add_development_dependency "rake", "~> 13.0"
|
34
35
|
spec.add_development_dependency "rspec", "~> 3.0"
|
35
36
|
spec.add_development_dependency "rspec-sidekiq"
|
37
|
+
spec.add_development_dependency "aws-sdk-glacier"
|
36
38
|
|
37
39
|
spec.add_development_dependency "activerecord", "~> 6"
|
38
|
-
spec.add_development_dependency "
|
40
|
+
spec.add_development_dependency "pg"
|
41
|
+
spec.add_development_dependency "vcr"
|
42
|
+
spec.add_development_dependency "webmock"
|
43
|
+
spec.add_development_dependency "dotenv"
|
44
|
+
spec.add_development_dependency "postgres-copy"
|
45
|
+
spec.add_development_dependency "timecop"
|
39
46
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tartarus-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karol Galanciak
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sidekiq
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: aws-sdk-glacier
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: activerecord
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,7 +109,77 @@ dependencies:
|
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '6'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
112
|
+
name: pg
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: vcr
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: webmock
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: dotenv
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: postgres-copy
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: timecop
|
99
183
|
requirement: !ruby/object:Gem::Requirement
|
100
184
|
requirements:
|
101
185
|
- - ">="
|
@@ -116,6 +200,7 @@ executables: []
|
|
116
200
|
extensions: []
|
117
201
|
extra_rdoc_files: []
|
118
202
|
files:
|
203
|
+
- ".env.sample"
|
119
204
|
- ".gitignore"
|
120
205
|
- ".rspec"
|
121
206
|
- ".travis.yml"
|
@@ -136,6 +221,7 @@ files:
|
|
136
221
|
- lib/tartarus/archive_model_without_tenant.rb
|
137
222
|
- lib/tartarus/archive_strategy.rb
|
138
223
|
- lib/tartarus/archive_strategy/delete_all.rb
|
224
|
+
- lib/tartarus/archive_strategy/delete_all_using_limit_in_batches.rb
|
139
225
|
- lib/tartarus/archive_strategy/delete_all_without_batches.rb
|
140
226
|
- lib/tartarus/archive_strategy/destroy_all.rb
|
141
227
|
- lib/tartarus/archive_strategy/destroy_all_without_batches.rb
|
@@ -143,6 +229,14 @@ files:
|
|
143
229
|
- lib/tartarus/rb.rb
|
144
230
|
- lib/tartarus/rb/version.rb
|
145
231
|
- lib/tartarus/registry.rb
|
232
|
+
- lib/tartarus/remote_storage.rb
|
233
|
+
- lib/tartarus/remote_storage/glacier.rb
|
234
|
+
- lib/tartarus/remote_storage/glacier/client.rb
|
235
|
+
- lib/tartarus/remote_storage/glacier/configuration.rb
|
236
|
+
- lib/tartarus/remote_storage/glacier/csv_export.rb
|
237
|
+
- lib/tartarus/remote_storage/glacier/file.rb
|
238
|
+
- lib/tartarus/remote_storage/glacier/register_upload.rb
|
239
|
+
- lib/tartarus/remote_storage/null.rb
|
146
240
|
- lib/tartarus/repository.rb
|
147
241
|
- lib/tartarus/schedule_archiving_model.rb
|
148
242
|
- lib/tartarus/sidekiq.rb
|