postmodern 0.0.11 → 0.1.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/ATTRIBUTION.md +5 -0
- data/Gemfile +1 -0
- data/README.md +75 -1
- data/examples/postmodern_archive.local +17 -0
- data/examples/postmodern_restore.local +14 -0
- data/lib/postmodern.rb +4 -0
- data/lib/postmodern/command.rb +36 -8
- data/lib/postmodern/db/adapter.rb +30 -0
- data/lib/postmodern/dummy.rb +6 -0
- data/lib/postmodern/runner.rb +8 -2
- data/lib/postmodern/vacuum/freeze.rb +33 -0
- data/lib/postmodern/vacuum/vacuum.rb +155 -0
- data/lib/postmodern/version.rb +1 -1
- data/lib/postmodern/wal/archive.rb +3 -4
- data/postmodern.gemspec +2 -0
- data/spec/features/archive_spec.rb +10 -2
- data/spec/features/dummy_spec.rb +26 -7
- data/spec/features/freeze_spec.rb +24 -0
- data/spec/features/restore_spec.rb +44 -0
- data/spec/features/vacuum_spec.rb +24 -0
- data/spec/postmodern/command_spec.rb +24 -0
- data/spec/postmodern/db/adapter_spec.rb +46 -0
- data/spec/postmodern/postmodern_spec.rb +10 -0
- data/spec/postmodern/runner_spec.rb +5 -0
- data/spec/postmodern/vacuum/freeze_spec.rb +59 -0
- data/spec/postmodern/vacuum/vacuum_spec.rb +186 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/logger.rb +9 -0
- metadata +38 -3
- data/lib/postmodern/wal.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1bc358882bdb3ac6f246d3967ac261076b4c334d
|
4
|
+
data.tar.gz: 9a3cf7820f70f5271f9ee570be8444bd8305d7e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 619b48a8a1b7b6c250455c143dbe2ac9465066269aea935760e51f7ac6ca9393aacb92a4234a39000fa230de7edb6a5261916e4ccb20c50af8b75ae329ba4e6b
|
7
|
+
data.tar.gz: 3e98b9eeaaa9e57886b00c653e871d2b9816024a17c4c96089abe22cdc6b582a4c8762b7490a5acff54483674296a0170e17dd3f092dfa4cd5429b2e3ef3924e
|
data/ATTRIBUTION.md
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -7,6 +7,11 @@ Tools for managing PostgreSQL databases.
|
|
7
7
|
|
8
8
|
* WAL archiving and restoration
|
9
9
|
|
10
|
+
## Dependencies
|
11
|
+
|
12
|
+
* libpq
|
13
|
+
* [pg gem](http://rubygems.org/gems/pg)
|
14
|
+
|
10
15
|
## Installation
|
11
16
|
|
12
17
|
```bash
|
@@ -18,10 +23,62 @@ the system's ruby, however that is installed.
|
|
18
23
|
|
19
24
|
## Usage
|
20
25
|
|
26
|
+
### Vacuuming and Vacuum Freezing
|
27
|
+
|
28
|
+
Postmodern's vacuum scripts run table by table, with various constraints
|
29
|
+
to limit the overhead of the process.
|
30
|
+
|
31
|
+
```
|
32
|
+
Usage: postmodern (vacuum|freeze) <options>
|
33
|
+
-U, --user USER Defaults to postgres
|
34
|
+
-p, --port PORT Defaults to 5432
|
35
|
+
-H, --host HOST Defaults to 127.0.0.1
|
36
|
+
-W, --password PASS
|
37
|
+
|
38
|
+
-t, --timeout TIMEOUT Halt after timeout minutes -- default 120
|
39
|
+
-d, --database DB Database to vacuum. Required.
|
40
|
+
|
41
|
+
-B, --tablesize BYTES minimum table size to vacuum -- default 1000000
|
42
|
+
-F, --freezeage AGE minimum freeze age -- default 10000000
|
43
|
+
-D, --costdelay DELAY vacuum_cost_delay setting in ms -- default 20
|
44
|
+
-L, --costlimit LIMIT vacuum_cost_limit setting -- default 2000
|
45
|
+
|
46
|
+
-h, --help Show this message
|
47
|
+
--version Show version
|
48
|
+
```
|
49
|
+
|
50
|
+
To run a vacuum:
|
51
|
+
|
52
|
+
```
|
53
|
+
postmodern vacuum -U postgres -p 5432 -d my_database
|
54
|
+
```
|
55
|
+
|
56
|
+
In order to run vacuum freeze:
|
57
|
+
|
58
|
+
```
|
59
|
+
postmodern freeze -U postgres -p 5432 -d my_database
|
60
|
+
```
|
61
|
+
|
62
|
+
These tasks are designed to be run regularly during a window of lower
|
63
|
+
database activity. They vacuum or vacuum freeze each table that requires
|
64
|
+
it (based on command line options). Before each operation, the scripts check
|
65
|
+
to make sure they have not gone longer than `--timeout` seconds.
|
66
|
+
|
21
67
|
### WAL archives
|
22
68
|
|
23
69
|
The wal archiving scripts packaged in this gem are intended to serve as
|
24
|
-
wrappers for YOUR archiving mechanism.
|
70
|
+
wrappers for YOUR archiving mechanism. Changing the settings for WAL
|
71
|
+
archiving in `postgresql.conf` or in `recovery.conf` require full restarts
|
72
|
+
of PostgreSQL—using Postmodern, you can configure PostgreSQL once and swap
|
73
|
+
in local scripts to do the actual work.
|
74
|
+
|
75
|
+
```
|
76
|
+
Usage: postmodern (archive|restore) <options>
|
77
|
+
-f, --filename FILE File name of xlog
|
78
|
+
-p, --path PATH Path of xlog file
|
79
|
+
-h, --help Show this message
|
80
|
+
--version Show version
|
81
|
+
```
|
25
82
|
|
26
83
|
In postgresql.conf
|
27
84
|
|
@@ -51,6 +108,13 @@ the relevant arguments either as $1, $2 or using the variables listed above.
|
|
51
108
|
`archive` will attempt to call a `postmodern_archive.local` script.
|
52
109
|
`restore` will attempt to call a `postmodern_restore.local` script.
|
53
110
|
|
111
|
+
see the [examples](https://github.com/wanelo/postmodern/tree/master/examples)
|
112
|
+
directory for example local scripts.
|
113
|
+
|
114
|
+
## Attribution & Thanks
|
115
|
+
|
116
|
+
Please see the [attribution](https://github.com/wanelo/postmodern/blob/master/ATTRIBUTION.md)
|
117
|
+
file for proper attribution and thanks.
|
54
118
|
|
55
119
|
## Contributing
|
56
120
|
|
@@ -59,3 +123,13 @@ the relevant arguments either as $1, $2 or using the variables listed above.
|
|
59
123
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
60
124
|
4. Push to the branch (`git push origin my-new-feature`)
|
61
125
|
5. Create new Pull Request
|
126
|
+
|
127
|
+
Contributions will not be accepted without tests. What should be a
|
128
|
+
feature and what a unit test is highly open to interpretation, however.
|
129
|
+
In some cases, a unit test may be easier and acceptable. In general,
|
130
|
+
at least one feature should be written for each new subcommand, even
|
131
|
+
if it just runs `--help`.
|
132
|
+
|
133
|
+
If in doubt, open an issue. If you don't receive a response to an issue
|
134
|
+
or a pull request, please mention one of the core committers of this
|
135
|
+
gem in a comment to make sure it doesn't get swallowed in an email abyss.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Local file for running postgres wal log archiving.
|
4
|
+
# Variables are set by postmodern and map to
|
5
|
+
# the following wal archiving conventions:
|
6
|
+
#
|
7
|
+
# WAL_ARCHIVE_PATH = %p
|
8
|
+
# WAL_ARCHIVE_FILE = %f
|
9
|
+
#
|
10
|
+
|
11
|
+
WAL_ARCHIVE_PATH=$1
|
12
|
+
WAL_ARCHIVE_FILE=$2
|
13
|
+
|
14
|
+
mkdir -p /var/pgsql/wal_archive && find /var/pgsql/wal_archive/ -mtime +1 \
|
15
|
+
| xargs rm -f \
|
16
|
+
&& test ! -f /var/pgsql/wal_archive/$WAL_ARCHIVE_FILE \
|
17
|
+
&& cp $WAL_ARCHIVE_PATH /var/pgsql/wal_archive/$WAL_ARCHIVE_FILE
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Local file for running postgres wal log archiving.
|
4
|
+
# Variables are set by postmodern and map to
|
5
|
+
# the following wal archiving conventions:
|
6
|
+
#
|
7
|
+
# WAL_ARCHIVE_PATH = %p
|
8
|
+
# WAL_ARCHIVE_FILE = %f
|
9
|
+
#
|
10
|
+
|
11
|
+
WAL_ARCHIVE_PATH=$1
|
12
|
+
WAL_ARCHIVE_FILE=$2
|
13
|
+
|
14
|
+
cp /var/pgsql/wal_archive/$WAL_ARCHIVE_FILE $WAL_ARCHIVE_PATH
|
data/lib/postmodern.rb
CHANGED
data/lib/postmodern/command.rb
CHANGED
@@ -2,18 +2,38 @@ require 'optparse'
|
|
2
2
|
|
3
3
|
module Postmodern
|
4
4
|
class Command
|
5
|
-
|
6
|
-
|
5
|
+
class << self
|
6
|
+
def inherited(subclass)
|
7
|
+
subclass.instance_variable_set(:@required_options, @required_options)
|
8
|
+
subclass.instance_variable_set(:@default_options, @default_options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def required_options
|
12
|
+
@required_options ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def required_option(*options)
|
16
|
+
required_options.concat(options)
|
17
|
+
required_options.uniq!
|
18
|
+
end
|
19
|
+
|
20
|
+
def default_options
|
21
|
+
@default_options ||= {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_option(key, value)
|
25
|
+
default_options[key] = value
|
26
|
+
end
|
7
27
|
end
|
8
28
|
|
9
29
|
def parser
|
10
|
-
OptionParser
|
30
|
+
raise "Command needs to define an OptionParser"
|
11
31
|
end
|
12
32
|
|
13
33
|
attr_reader :options
|
14
34
|
|
15
35
|
def initialize(args)
|
16
|
-
@options =
|
36
|
+
@options = self.class.default_options.dup
|
17
37
|
|
18
38
|
parse_args(args)
|
19
39
|
validate!
|
@@ -23,15 +43,23 @@ module Postmodern
|
|
23
43
|
end
|
24
44
|
|
25
45
|
def validate!
|
26
|
-
if
|
27
|
-
puts
|
28
|
-
|
46
|
+
if missing_params.any?
|
47
|
+
puts "Missing #{missing_params.join(', ')}"
|
48
|
+
usage!
|
29
49
|
end
|
30
50
|
end
|
31
51
|
|
52
|
+
def missing_params
|
53
|
+
self.class.required_options - self.options.keys
|
54
|
+
end
|
55
|
+
|
32
56
|
def parse_args(args)
|
33
57
|
parser.parse!(args)
|
34
|
-
|
58
|
+
end
|
59
|
+
|
60
|
+
def usage!
|
61
|
+
puts parser.to_s
|
62
|
+
exit 1
|
35
63
|
end
|
36
64
|
end
|
37
65
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'pg'
|
2
|
+
|
3
|
+
module Postmodern
|
4
|
+
module DB
|
5
|
+
class Adapter
|
6
|
+
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
11
|
+
end
|
12
|
+
|
13
|
+
def pg_adapter
|
14
|
+
@pg_adapter ||= PG.connect(db_configuration)
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute(sql)
|
18
|
+
pg_adapter.exec(sql)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def db_configuration
|
24
|
+
db_configuration = {}.merge(config)
|
25
|
+
db_configuration.delete(:password) unless config[:password]
|
26
|
+
db_configuration
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/postmodern/dummy.rb
CHANGED
@@ -2,6 +2,11 @@ require 'postmodern/command'
|
|
2
2
|
|
3
3
|
module Postmodern
|
4
4
|
class Dummy < Command
|
5
|
+
def run
|
6
|
+
puts parser
|
7
|
+
exit 1
|
8
|
+
end
|
9
|
+
|
5
10
|
def parser
|
6
11
|
@parser ||= OptionParser.new do |opts|
|
7
12
|
opts.banner = "Usage: postmodern <command> <options>"
|
@@ -10,6 +15,7 @@ module Postmodern
|
|
10
15
|
opts.separator "Available commands:"
|
11
16
|
opts.separator " archive"
|
12
17
|
opts.separator " restore"
|
18
|
+
opts.separator " vacuum"
|
13
19
|
opts.separator ""
|
14
20
|
opts.separator "Options:"
|
15
21
|
|
data/lib/postmodern/runner.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
require 'postmodern
|
1
|
+
require 'postmodern'
|
2
|
+
require 'postmodern/wal/archive'
|
3
|
+
require 'postmodern/wal/restore'
|
4
|
+
require 'postmodern/vacuum/vacuum'
|
5
|
+
require 'postmodern/vacuum/freeze'
|
2
6
|
require 'postmodern/dummy'
|
3
7
|
|
4
8
|
module Postmodern
|
@@ -8,7 +12,9 @@ module Postmodern
|
|
8
12
|
DEFAULT_COMMAND = Dummy
|
9
13
|
COMMAND_MAP = {
|
10
14
|
'archive' => WAL::Archive,
|
11
|
-
'restore' => WAL::Restore
|
15
|
+
'restore' => WAL::Restore,
|
16
|
+
'vacuum' => Vacuum::Vacuum,
|
17
|
+
'freeze' => Vacuum::Freeze
|
12
18
|
}.freeze
|
13
19
|
|
14
20
|
def self.run(args)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'postmodern/vacuum/vacuum'
|
2
|
+
|
3
|
+
module Postmodern
|
4
|
+
module Vacuum
|
5
|
+
class Freeze < Postmodern::Vacuum::Vacuum
|
6
|
+
|
7
|
+
def vacuum_statement table_name
|
8
|
+
"VACUUM FREEZE ANALYZE %s" % table_name
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def table_sql
|
14
|
+
<<-SQL.gsub(/^\s{8}/, '')
|
15
|
+
WITH tabfreeze AS (
|
16
|
+
SELECT pg_class.oid::regclass AS full_table_name,
|
17
|
+
age(relfrozenxid)as freeze_age,
|
18
|
+
pg_relation_size(pg_class.oid)
|
19
|
+
FROM pg_class JOIN pg_namespace ON relnamespace = pg_namespace.oid
|
20
|
+
WHERE nspname not in ('pg_catalog', 'information_schema')
|
21
|
+
AND nspname NOT LIKE 'pg_temp%'
|
22
|
+
AND relkind = 'r'
|
23
|
+
)
|
24
|
+
SELECT full_table_name
|
25
|
+
FROM tabfreeze
|
26
|
+
WHERE freeze_age > #{options[:freezeage]}
|
27
|
+
ORDER BY freeze_age DESC
|
28
|
+
LIMIT 1000;
|
29
|
+
SQL
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'postmodern/command'
|
2
|
+
require 'postmodern/db/adapter'
|
3
|
+
|
4
|
+
module Postmodern
|
5
|
+
module Vacuum
|
6
|
+
class Vacuum < Postmodern::Command
|
7
|
+
default_option :timeout, 120
|
8
|
+
default_option :pause, 10
|
9
|
+
default_option :tablesize, 1000000
|
10
|
+
default_option :freezeage, 10000000
|
11
|
+
default_option :costdelay, 20
|
12
|
+
default_option :costlimit, 2000
|
13
|
+
default_option :user, 'postgres'
|
14
|
+
default_option :port, 5432
|
15
|
+
default_option :host, '127.0.0.1'
|
16
|
+
|
17
|
+
required_option :database
|
18
|
+
|
19
|
+
def parser
|
20
|
+
@parser ||= OptionParser.new do |opts|
|
21
|
+
opts.banner = "Usage: postmodern (vacuum|freeze) <options>"
|
22
|
+
|
23
|
+
opts.on('-U', '--user USER', 'Defaults to postgres') do |opt|
|
24
|
+
self.options[:user] = opt
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-p', '--port PORT', 'Defaults to 5432') do |opt|
|
28
|
+
self.options[:port] = opt
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on('-H', '--host HOST', 'Defaults to 127.0.0.1') do |opt|
|
32
|
+
self.options[:host] = opt
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on('-W', '--password PASS') do |opt|
|
36
|
+
self.options[:password] = opt
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.separator ''
|
40
|
+
|
41
|
+
opts.on('-t', '--timeout TIMEOUT', 'Halt after timeout minutes -- default 120') do |opt|
|
42
|
+
self.options[:timeout] = opt
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on('-d', '--database DB', 'Database to vacuum. Required.') do |opt|
|
46
|
+
self.options[:database] = opt
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.separator ''
|
50
|
+
|
51
|
+
opts.on('-B', '--tablesize BYTES', 'minimum table size to vacuum -- default 1000000') do |opt|
|
52
|
+
self.options[:tablesize] = opt
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-F', '--freezeage AGE', 'minimum freeze age -- default 10000000') do |opt|
|
56
|
+
self.options[:freezeage] = opt
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('-D', '--costdelay DELAY', 'vacuum_cost_delay setting in ms -- default 20') do |opt|
|
60
|
+
self.options[:costdelay] = opt
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on('-L', '--costlimit LIMIT', 'vacuum_cost_limit setting -- default 2000') do |opt|
|
64
|
+
self.options[:costlimit] = opt
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.separator ''
|
68
|
+
|
69
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
70
|
+
puts opts
|
71
|
+
exit
|
72
|
+
end
|
73
|
+
|
74
|
+
opts.on_tail("--version", "Show version") do
|
75
|
+
require 'postmodern/version'
|
76
|
+
puts Postmodern::VERSION
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
attr_reader :adapter, :start_time
|
83
|
+
|
84
|
+
def initialize(args)
|
85
|
+
@start_time = Time.now
|
86
|
+
super(args)
|
87
|
+
end
|
88
|
+
|
89
|
+
def run
|
90
|
+
configure_vacuum_cost
|
91
|
+
vacuum
|
92
|
+
end
|
93
|
+
|
94
|
+
def configure_vacuum_cost
|
95
|
+
adapter.execute("SET vacuum_cost_delay = '%d ms'" % options[:costdelay])
|
96
|
+
adapter.execute("SET vacuum_cost_limit = '%d'" % options[:costlimit])
|
97
|
+
end
|
98
|
+
|
99
|
+
def vacuum
|
100
|
+
tables_to_vacuum.each do |table|
|
101
|
+
Postmodern.logger.info "Vacuuming #{table}"
|
102
|
+
adapter.execute(vacuum_statement(table))
|
103
|
+
if timedout?
|
104
|
+
Postmodern.logger.warn "Vacuuming timed out"
|
105
|
+
break
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def vacuum_statement table_name
|
111
|
+
"VACUUM ANALYZE %s" % table_name
|
112
|
+
end
|
113
|
+
|
114
|
+
def timedout?
|
115
|
+
Time.now >= start_time + (options[:timeout].to_i * 60)
|
116
|
+
end
|
117
|
+
|
118
|
+
def tables_to_vacuum
|
119
|
+
table_res = adapter.execute(table_sql)
|
120
|
+
table_res.map{|f| f['full_table_name']}
|
121
|
+
end
|
122
|
+
|
123
|
+
def adapter
|
124
|
+
@adapter ||= Postmodern::DB::Adapter.new({
|
125
|
+
dbname: options[:database],
|
126
|
+
port: options[:port],
|
127
|
+
host: options[:host],
|
128
|
+
user: options[:user],
|
129
|
+
password: options[:password]
|
130
|
+
})
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
def table_sql
|
136
|
+
<<-SQL.gsub(/^\s{8}/, '')
|
137
|
+
WITH deadrow_tables AS (
|
138
|
+
SELECT relid::regclass as full_table_name,
|
139
|
+
((n_dead_tup::numeric) / ( n_live_tup + 1 )) as dead_pct,
|
140
|
+
pg_relation_size(relid) as table_bytes
|
141
|
+
FROM pg_stat_user_tables
|
142
|
+
WHERE n_dead_tup > 100
|
143
|
+
AND ( (now() - last_autovacuum) > INTERVAL '1 hour'
|
144
|
+
OR last_autovacuum IS NULL )
|
145
|
+
)
|
146
|
+
SELECT full_table_name
|
147
|
+
FROM deadrow_tables
|
148
|
+
WHERE dead_pct > 0.05
|
149
|
+
AND table_bytes > #{options[:tablesize]}
|
150
|
+
ORDER BY dead_pct DESC, table_bytes DESC;
|
151
|
+
SQL
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/postmodern/version.rb
CHANGED
@@ -3,15 +3,14 @@ require 'postmodern/command'
|
|
3
3
|
module Postmodern
|
4
4
|
module WAL
|
5
5
|
class Archive < Postmodern::Command
|
6
|
-
|
7
|
-
required_options << :path
|
6
|
+
required_option :filename, :path
|
8
7
|
|
9
8
|
def parser
|
10
9
|
@parser ||= OptionParser.new do |opts|
|
11
10
|
opts.banner = "Usage: postmodern (archive|restore) <options>"
|
12
11
|
|
13
12
|
opts.on('-f', '--filename FILE', 'File name of xlog') do |o|
|
14
|
-
self.options[:
|
13
|
+
self.options[:filename] = o
|
15
14
|
end
|
16
15
|
|
17
16
|
opts.on('-p', '--path PATH', 'Path of xlog file') do |o|
|
@@ -49,7 +48,7 @@ module Postmodern
|
|
49
48
|
end
|
50
49
|
|
51
50
|
def filename
|
52
|
-
@options[:
|
51
|
+
@options[:filename]
|
53
52
|
end
|
54
53
|
|
55
54
|
def local_script_exists?
|
data/postmodern.gemspec
CHANGED
@@ -37,7 +37,11 @@ Usage: postmodern (archive|restore) <options>
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'prints usage' do
|
40
|
-
expect(command).to
|
40
|
+
expect(command).to match(Regexp.escape(usage))
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'includes missing params' do
|
44
|
+
expect(command).to match('Missing path')
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
@@ -49,7 +53,11 @@ Usage: postmodern (archive|restore) <options>
|
|
49
53
|
end
|
50
54
|
|
51
55
|
it 'prints usage' do
|
52
|
-
expect(command).to
|
56
|
+
expect(command).to match(Regexp.escape(usage))
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'includes missing params' do
|
60
|
+
expect(command).to match('Missing filename')
|
53
61
|
end
|
54
62
|
end
|
55
63
|
end
|
data/spec/features/dummy_spec.rb
CHANGED
@@ -1,24 +1,43 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'postmodern/version'
|
3
3
|
|
4
|
-
describe 'dummy
|
5
|
-
|
6
|
-
|
4
|
+
describe 'dummy' do
|
5
|
+
let(:usage) {
|
6
|
+
<<-END
|
7
7
|
Usage: postmodern <command> <options>
|
8
8
|
|
9
9
|
Available commands:
|
10
10
|
archive
|
11
11
|
restore
|
12
|
+
vacuum
|
12
13
|
|
13
14
|
Options:
|
14
15
|
-h, --help Show this message
|
15
16
|
--version Show version
|
16
17
|
END
|
18
|
+
}
|
19
|
+
|
20
|
+
describe 'help' do
|
21
|
+
it 'responds with usage info' do
|
22
|
+
expect(`bin/postmodern --help`).to eq(usage)
|
23
|
+
end
|
17
24
|
end
|
18
|
-
end
|
19
25
|
|
20
|
-
describe '
|
21
|
-
|
22
|
-
|
26
|
+
describe 'version' do
|
27
|
+
it 'responds with the Postmodern version' do
|
28
|
+
expect(`bin/postmodern --version`).to match(Postmodern::VERSION)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'argument catchall' do
|
33
|
+
let(:command) { `bin/postmodern dlaskfjdflf` }
|
34
|
+
|
35
|
+
it 'exits 1' do
|
36
|
+
expect { command }.to have_exit_status(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'prints usage' do
|
40
|
+
expect(command).to eq(usage)
|
41
|
+
end
|
23
42
|
end
|
24
43
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'freeze help' do
|
4
|
+
it 'responds' do
|
5
|
+
expect(`bin/postmodern freeze --help`).to eq <<-END
|
6
|
+
Usage: postmodern (vacuum|freeze) <options>
|
7
|
+
-U, --user USER Defaults to postgres
|
8
|
+
-p, --port PORT Defaults to 5432
|
9
|
+
-H, --host HOST Defaults to 127.0.0.1
|
10
|
+
-W, --password PASS
|
11
|
+
|
12
|
+
-t, --timeout TIMEOUT Halt after timeout minutes -- default 120
|
13
|
+
-d, --database DB Database to vacuum. Required.
|
14
|
+
|
15
|
+
-B, --tablesize BYTES minimum table size to vacuum -- default 1000000
|
16
|
+
-F, --freezeage AGE minimum freeze age -- default 10000000
|
17
|
+
-D, --costdelay DELAY vacuum_cost_delay setting in ms -- default 20
|
18
|
+
-L, --costlimit LIMIT vacuum_cost_limit setting -- default 2000
|
19
|
+
|
20
|
+
-h, --help Show this message
|
21
|
+
--version Show version
|
22
|
+
END
|
23
|
+
end
|
24
|
+
end
|
@@ -15,10 +15,54 @@ end
|
|
15
15
|
describe 'restore' do
|
16
16
|
let(:command) { `bin/postmodern restore --path filepath --filename filename` }
|
17
17
|
|
18
|
+
let(:usage) { <<-END
|
19
|
+
Usage: postmodern (archive|restore) <options>
|
20
|
+
-f, --filename FILE File name of xlog
|
21
|
+
-p, --path PATH Path of xlog file
|
22
|
+
-h, --help Show this message
|
23
|
+
--version Show version
|
24
|
+
END
|
25
|
+
}
|
26
|
+
|
27
|
+
|
18
28
|
before do
|
19
29
|
double_cmd('postmodern_restore.local')
|
20
30
|
end
|
21
31
|
|
32
|
+
describe 'validations' do
|
33
|
+
context 'with no --path' do
|
34
|
+
let(:command) { `bin/postmodern restore --filename filename` }
|
35
|
+
|
36
|
+
it 'fails' do
|
37
|
+
expect { command }.to have_exit_status(1)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'prints usage' do
|
41
|
+
expect(command).to match(Regexp.escape(usage))
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'includes missing params' do
|
45
|
+
expect(command).to match('Missing path')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'with no --filename' do
|
50
|
+
let(:command) { `bin/postmodern restore --path path` }
|
51
|
+
|
52
|
+
it 'fails' do
|
53
|
+
expect { command }.to have_exit_status(1)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'prints usage' do
|
57
|
+
expect(command).to match(Regexp.escape(usage))
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'includes missing params' do
|
61
|
+
expect(command).to match('Missing filename')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
22
66
|
context 'when local script is present' do
|
23
67
|
before { double_cmd('which', exit: 0) }
|
24
68
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'vacuum help' do
|
4
|
+
it 'responds' do
|
5
|
+
expect(`bin/postmodern vacuum --help`).to eq <<-END
|
6
|
+
Usage: postmodern (vacuum|freeze) <options>
|
7
|
+
-U, --user USER Defaults to postgres
|
8
|
+
-p, --port PORT Defaults to 5432
|
9
|
+
-H, --host HOST Defaults to 127.0.0.1
|
10
|
+
-W, --password PASS
|
11
|
+
|
12
|
+
-t, --timeout TIMEOUT Halt after timeout minutes -- default 120
|
13
|
+
-d, --database DB Database to vacuum. Required.
|
14
|
+
|
15
|
+
-B, --tablesize BYTES minimum table size to vacuum -- default 1000000
|
16
|
+
-F, --freezeage AGE minimum freeze age -- default 10000000
|
17
|
+
-D, --costdelay DELAY vacuum_cost_delay setting in ms -- default 20
|
18
|
+
-L, --costlimit LIMIT vacuum_cost_limit setting -- default 2000
|
19
|
+
|
20
|
+
-h, --help Show this message
|
21
|
+
--version Show version
|
22
|
+
END
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'postmodern/command'
|
3
|
+
|
4
|
+
describe Postmodern::Command do
|
5
|
+
let(:option_parser) { double(parse!: true, to_s: nil) }
|
6
|
+
subject(:command_class) { Class.new(Postmodern::Command) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
local_scope_option_parser = option_parser
|
10
|
+
command_class.send(:define_method, :parser) { local_scope_option_parser }
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
describe '#usage!' do
|
15
|
+
it 'exits 1' do
|
16
|
+
expect { command_class.new([]).usage! }.to raise_error(SystemExit)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'prints parser' do
|
20
|
+
expect { command_class.new([]).usage! }.to raise_error(SystemExit)
|
21
|
+
expect(option_parser).to have_received(:to_s)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'postmodern/db/adapter'
|
3
|
+
|
4
|
+
describe Postmodern::DB::Adapter do
|
5
|
+
subject(:adapter) { Postmodern::DB::Adapter.new(configuration) }
|
6
|
+
|
7
|
+
let(:password) { 'secure...' }
|
8
|
+
let(:configuration) do
|
9
|
+
{
|
10
|
+
stuff: 'things'
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#adapter' do
|
15
|
+
context 'with password' do
|
16
|
+
let(:configuration) { {stuff: 'things', password: 'password'} }
|
17
|
+
|
18
|
+
it 'initializes a PG adapter' do
|
19
|
+
expect(PG).to receive(:connect).with({stuff: 'things', password: 'password'})
|
20
|
+
adapter.pg_adapter
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'without password' do
|
25
|
+
let(:configuration) { {stuff: 'things', password: nil} }
|
26
|
+
|
27
|
+
it 'initializes a PG adapter without password params' do
|
28
|
+
expect(PG).to receive(:connect).with({stuff: 'things'})
|
29
|
+
adapter.pg_adapter
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#execute' do
|
35
|
+
let(:pg_adapter) { double }
|
36
|
+
|
37
|
+
before do
|
38
|
+
allow(adapter).to receive(:pg_adapter).and_return(pg_adapter)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'delegates to PG adapter' do
|
42
|
+
expect(pg_adapter).to receive(:exec).with('blah;')
|
43
|
+
adapter.execute('blah;')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -12,6 +12,11 @@ describe Postmodern::Runner do
|
|
12
12
|
expect(Postmodern::Runner.command_for('restore')).to be Postmodern::WAL::Restore
|
13
13
|
end
|
14
14
|
|
15
|
+
|
16
|
+
it 'chooses vacuumer' do
|
17
|
+
expect(Postmodern::Runner.command_for('vacuum')).to be Postmodern::Vacuum::Vacuum
|
18
|
+
end
|
19
|
+
|
15
20
|
it 'defaults to dummy' do
|
16
21
|
expect(Postmodern::Runner.command_for('ljfaldf')).to be Postmodern::Dummy
|
17
22
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'postmodern/vacuum/freeze'
|
3
|
+
|
4
|
+
describe Postmodern::Vacuum::Freeze do
|
5
|
+
let(:adapter) { double }
|
6
|
+
let(:args) { %w(-d db) }
|
7
|
+
subject(:command) { Postmodern::Vacuum::Freeze.new(args) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
allow(Postmodern).to receive(:logger).and_return(FakeLogger.new)
|
11
|
+
allow(adapter).to receive(:execute)
|
12
|
+
allow(command).to receive(:adapter).and_return(adapter)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#tables_to_vacuum' do
|
16
|
+
let(:args) { %w(-d mydb -F 12345) }
|
17
|
+
|
18
|
+
it 'finds list of tables to vacuum' do
|
19
|
+
result = [
|
20
|
+
{'full_table_name' => 'table1'},
|
21
|
+
{'full_table_name' => 'table2'},
|
22
|
+
]
|
23
|
+
allow(command).to receive(:adapter).and_return(adapter)
|
24
|
+
expect(adapter).to receive(:execute).with(%Q{WITH tabfreeze AS (
|
25
|
+
SELECT pg_class.oid::regclass AS full_table_name,
|
26
|
+
age(relfrozenxid)as freeze_age,
|
27
|
+
pg_relation_size(pg_class.oid)
|
28
|
+
FROM pg_class JOIN pg_namespace ON relnamespace = pg_namespace.oid
|
29
|
+
WHERE nspname not in ('pg_catalog', 'information_schema')
|
30
|
+
AND nspname NOT LIKE 'pg_temp%'
|
31
|
+
AND relkind = 'r'
|
32
|
+
)
|
33
|
+
SELECT full_table_name
|
34
|
+
FROM tabfreeze
|
35
|
+
WHERE freeze_age > 12345
|
36
|
+
ORDER BY freeze_age DESC
|
37
|
+
LIMIT 1000;
|
38
|
+
}).and_return(result)
|
39
|
+
|
40
|
+
expect(command.tables_to_vacuum).to eq(%w(table1 table2))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#vacuum' do
|
45
|
+
let(:tables_to_vacuum) { %w(table1 table2 table3) }
|
46
|
+
|
47
|
+
before do
|
48
|
+
allow(Postmodern::DB::Adapter).to receive(:new).and_return(adapter)
|
49
|
+
allow(command).to receive(:tables_to_vacuum).and_return(tables_to_vacuum)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "vacuums each table" do
|
53
|
+
command.vacuum
|
54
|
+
tables_to_vacuum.each do |table|
|
55
|
+
expect(adapter).to have_received(:execute).with("VACUUM FREEZE ANALYZE %s" % table)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'postmodern/vacuum/vacuum'
|
3
|
+
require 'timecop'
|
4
|
+
|
5
|
+
describe Postmodern::Vacuum::Vacuum do
|
6
|
+
let(:args) { %w(-d mydb) }
|
7
|
+
let(:adapter) { double }
|
8
|
+
subject(:command) { Postmodern::Vacuum::Vacuum.new(args) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
allow(Postmodern).to receive(:logger).and_return(FakeLogger.new)
|
12
|
+
allow(adapter).to receive(:execute)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#options" do
|
16
|
+
{
|
17
|
+
user: 'postgres',
|
18
|
+
port: 5432,
|
19
|
+
host: '127.0.0.1',
|
20
|
+
timeout: 120,
|
21
|
+
pause: 10,
|
22
|
+
tablesize: 1000000,
|
23
|
+
freezeage: 10000000,
|
24
|
+
costdelay: 20,
|
25
|
+
costlimit: 2000
|
26
|
+
}.each do |option, default|
|
27
|
+
it "defaults on #{option}" do
|
28
|
+
expect(command.options[option]).to eq(default)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'validations' do
|
34
|
+
let(:usage) { double(usage!: '') }
|
35
|
+
|
36
|
+
before do
|
37
|
+
allow_any_instance_of(Postmodern::Vacuum::Vacuum).
|
38
|
+
to receive(:usage!) { usage.usage! }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'database' do
|
42
|
+
it 'requires database' do
|
43
|
+
Postmodern::Vacuum::Vacuum.new([])
|
44
|
+
expect(usage).to have_received(:usage!)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#run" do
|
50
|
+
let(:args) { %w(--database mydb) }
|
51
|
+
|
52
|
+
before do
|
53
|
+
allow(command).to receive(:adapter).and_return(adapter)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'executes vacuum operations in order' do
|
57
|
+
expect(command).to receive(:configure_vacuum_cost).once
|
58
|
+
expect(command).to receive(:vacuum).once
|
59
|
+
command.run
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#adapter' do
|
64
|
+
before { allow(adapter).to receive(:execute) }
|
65
|
+
|
66
|
+
it 'instantiates a DB::Adapter' do
|
67
|
+
expect(Postmodern::DB::Adapter).to receive(:new).once.with({
|
68
|
+
dbname: 'mydb',
|
69
|
+
port: 5432,
|
70
|
+
host: '127.0.0.1',
|
71
|
+
user: 'postgres',
|
72
|
+
password: nil
|
73
|
+
}).and_return(adapter)
|
74
|
+
command.adapter
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when password is present' do
|
78
|
+
let(:args) { %w(--password mypass --database mydb) }
|
79
|
+
it 'passes through to adapter' do
|
80
|
+
expect(Postmodern::DB::Adapter).to receive(:new).once.with({
|
81
|
+
dbname: 'mydb',
|
82
|
+
port: 5432,
|
83
|
+
host: '127.0.0.1',
|
84
|
+
user: 'postgres',
|
85
|
+
password: 'mypass'
|
86
|
+
}).and_return(adapter)
|
87
|
+
command.adapter
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#configure_vacuum_cost' do
|
93
|
+
before do
|
94
|
+
allow(command).to receive(:adapter).and_return(adapter)
|
95
|
+
command.configure_vacuum_cost
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'sets vacuum cost delay' do
|
99
|
+
expect(adapter).to have_received(:execute).with("SET vacuum_cost_delay = '#{command.options[:costdelay]} ms'")
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'sets vacuum cost limit' do
|
103
|
+
expect(adapter).to have_received(:execute).with("SET vacuum_cost_limit = '#{command.options[:costlimit]}'")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#tables_to_vacuum' do
|
108
|
+
let(:args) { %w(-d mydb -B 12345) }
|
109
|
+
|
110
|
+
it 'finds list of tables to vacuum' do
|
111
|
+
result = [
|
112
|
+
{ 'full_table_name' => 'table1'},
|
113
|
+
{ 'full_table_name' => 'table2'},
|
114
|
+
]
|
115
|
+
allow(command).to receive(:adapter).and_return(adapter)
|
116
|
+
expect(adapter).to receive(:execute).with(%Q{WITH deadrow_tables AS (
|
117
|
+
SELECT relid::regclass as full_table_name,
|
118
|
+
((n_dead_tup::numeric) / ( n_live_tup + 1 )) as dead_pct,
|
119
|
+
pg_relation_size(relid) as table_bytes
|
120
|
+
FROM pg_stat_user_tables
|
121
|
+
WHERE n_dead_tup > 100
|
122
|
+
AND ( (now() - last_autovacuum) > INTERVAL '1 hour'
|
123
|
+
OR last_autovacuum IS NULL )
|
124
|
+
)
|
125
|
+
SELECT full_table_name
|
126
|
+
FROM deadrow_tables
|
127
|
+
WHERE dead_pct > 0.05
|
128
|
+
AND table_bytes > 12345
|
129
|
+
ORDER BY dead_pct DESC, table_bytes DESC;
|
130
|
+
}).and_return(result)
|
131
|
+
|
132
|
+
expect(command.tables_to_vacuum).to eq(%w(table1 table2))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "#vacuum" do
|
137
|
+
before do
|
138
|
+
allow(Postmodern::DB::Adapter).to receive(:new).and_return(adapter)
|
139
|
+
allow(command).to receive(:tables_to_vacuum).and_return(tables_to_vacuum)
|
140
|
+
end
|
141
|
+
|
142
|
+
let(:tables_to_vacuum) { %w(table1 table2 table3) }
|
143
|
+
|
144
|
+
it "vacuums each table" do
|
145
|
+
command.vacuum
|
146
|
+
tables_to_vacuum.each do |table|
|
147
|
+
expect(adapter).to have_received(:execute).with("VACUUM ANALYZE %s" % table)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
it "exits prematurely with a timeout and analyzes first table" do
|
152
|
+
allow(command).to receive(:timedout?).and_return(true)
|
153
|
+
command.vacuum
|
154
|
+
expect(adapter).to have_received(:execute).with("VACUUM ANALYZE %s" % tables_to_vacuum[0])
|
155
|
+
expect(adapter).not_to have_received(:execute).with("VACUUM ANALYZE %s" % tables_to_vacuum[1])
|
156
|
+
expect(adapter).not_to have_received(:execute).with("VACUUM ANALYZE %s" % tables_to_vacuum[2])
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe '#timedout?' do
|
161
|
+
let(:time) { Time.now }
|
162
|
+
let(:args) { %w(--d db -t 15) }
|
163
|
+
|
164
|
+
before do
|
165
|
+
Timecop.freeze time do
|
166
|
+
command # ensure command is initialized Now
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context 'current time is greater than timeout threshold since initialization' do
|
171
|
+
it 'is true' do
|
172
|
+
Timecop.freeze time + (15 * 60) do
|
173
|
+
expect(command.timedout?).to be true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'current time is less than timeout threshold since initialization' do
|
179
|
+
it 'is false' do
|
180
|
+
Timecop.freeze time + (15 * 60 - 1) do
|
181
|
+
expect(command.timedout?).to be false
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'aruba/rspec'
|
2
2
|
require 'pry'
|
3
|
+
require 'timecop'
|
4
|
+
|
5
|
+
require 'support/logger'
|
3
6
|
|
4
7
|
RSpec.configure do |config|
|
5
8
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
@@ -16,5 +19,6 @@ RSpec.configure do |config|
|
|
16
19
|
|
17
20
|
config.after :each do
|
18
21
|
Aruba::RSpec.teardown
|
22
|
+
Timecop.return
|
19
23
|
end
|
20
24
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postmodern
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Saxby
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pg
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -49,28 +63,41 @@ files:
|
|
49
63
|
- ".gitignore"
|
50
64
|
- ".rspec"
|
51
65
|
- ".travis.yml"
|
66
|
+
- ATTRIBUTION.md
|
52
67
|
- Gemfile
|
53
68
|
- Guardfile
|
54
69
|
- LICENSE.txt
|
55
70
|
- README.md
|
56
71
|
- Rakefile
|
57
72
|
- bin/postmodern
|
73
|
+
- examples/postmodern_archive.local
|
74
|
+
- examples/postmodern_restore.local
|
58
75
|
- lib/postmodern.rb
|
59
76
|
- lib/postmodern/command.rb
|
77
|
+
- lib/postmodern/db/adapter.rb
|
60
78
|
- lib/postmodern/dummy.rb
|
61
79
|
- lib/postmodern/runner.rb
|
80
|
+
- lib/postmodern/vacuum/freeze.rb
|
81
|
+
- lib/postmodern/vacuum/vacuum.rb
|
62
82
|
- lib/postmodern/version.rb
|
63
|
-
- lib/postmodern/wal.rb
|
64
83
|
- lib/postmodern/wal/archive.rb
|
65
84
|
- lib/postmodern/wal/restore.rb
|
66
85
|
- postmodern.gemspec
|
67
86
|
- spec/features/archive_spec.rb
|
68
87
|
- spec/features/dummy_spec.rb
|
88
|
+
- spec/features/freeze_spec.rb
|
69
89
|
- spec/features/restore_spec.rb
|
90
|
+
- spec/features/vacuum_spec.rb
|
91
|
+
- spec/postmodern/command_spec.rb
|
92
|
+
- spec/postmodern/db/adapter_spec.rb
|
93
|
+
- spec/postmodern/postmodern_spec.rb
|
70
94
|
- spec/postmodern/runner_spec.rb
|
95
|
+
- spec/postmodern/vacuum/freeze_spec.rb
|
96
|
+
- spec/postmodern/vacuum/vacuum_spec.rb
|
71
97
|
- spec/postmodern/wal/archive_spec.rb
|
72
98
|
- spec/postmodern/wal/restore_spec.rb
|
73
99
|
- spec/spec_helper.rb
|
100
|
+
- spec/support/logger.rb
|
74
101
|
homepage: https://github.com/wanelo/postmodern
|
75
102
|
licenses:
|
76
103
|
- MIT
|
@@ -98,9 +125,17 @@ summary: Tools for managing PostgreSQL
|
|
98
125
|
test_files:
|
99
126
|
- spec/features/archive_spec.rb
|
100
127
|
- spec/features/dummy_spec.rb
|
128
|
+
- spec/features/freeze_spec.rb
|
101
129
|
- spec/features/restore_spec.rb
|
130
|
+
- spec/features/vacuum_spec.rb
|
131
|
+
- spec/postmodern/command_spec.rb
|
132
|
+
- spec/postmodern/db/adapter_spec.rb
|
133
|
+
- spec/postmodern/postmodern_spec.rb
|
102
134
|
- spec/postmodern/runner_spec.rb
|
135
|
+
- spec/postmodern/vacuum/freeze_spec.rb
|
136
|
+
- spec/postmodern/vacuum/vacuum_spec.rb
|
103
137
|
- spec/postmodern/wal/archive_spec.rb
|
104
138
|
- spec/postmodern/wal/restore_spec.rb
|
105
139
|
- spec/spec_helper.rb
|
140
|
+
- spec/support/logger.rb
|
106
141
|
has_rdoc:
|
data/lib/postmodern/wal.rb
DELETED