pgslice 0.2.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 10c9e21963650fa68dd402f3c690ab0b89300bfb
4
- data.tar.gz: 5fe1cea0ef611008c95dbf06eae19e766dbf679e
3
+ metadata.gz: 1e344dce1fde075ab0621622d3f929e2c6677026
4
+ data.tar.gz: 9fe6e7825852fb37692ff019908dfb82e5cf4095
5
5
  SHA512:
6
- metadata.gz: 6f879dec8f413fd90eed3f911aacaedf5dda87036d490cab18d14fb1447439114a2359db4f15a95e3f713bcd0bf2bcad10f65c96c69537b83ed4172820b2d582
7
- data.tar.gz: 74189c73cdd2e6cf2549b31524c9e64103719d78135708bc2d992a118a0413a1b6d63cc4dc22659427717938fde3be5b8caab53d17af6d3088628697cc94256e
6
+ metadata.gz: a684bbbbc65be40c4a42850f0b54af657371649efef72f9f89337a8fae4ac046bd1fe758f1632ff2fc15c3a3d1e2c6feec7f2eaa70e13d9f815565993ff0e85a
7
+ data.tar.gz: 6b5e6fe5958f81085c8ec9a3d0d0161e35aa29f616b2db32dca993b520e14178face97dbe43133dc919b750b24a220cfe5819cd1732f6c64307cf81f6ee22363
@@ -1,3 +1,9 @@
1
+ ## 0.2.1
2
+
3
+ - Added `--where` option to `fill`
4
+ - Fixed partition detection with `fill`
5
+ - Fixed error for columns named `user` with `fill`
6
+
1
7
  ## 0.2.0
2
8
 
3
9
  - Switched to new trigger, which is about 20% faster
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pgslice
2
2
 
3
- Postgres partitioning as easy as pie. Works great for both new and existing tables, with zero downtime and minimal app changes.
3
+ Postgres partitioning as easy as pie. Works great for both new and existing tables, with zero downtime and minimal app changes. Archive older data on a rolling basis to keep your database size under control.
4
4
 
5
5
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
6
6
 
@@ -68,7 +68,12 @@ This will give you the `pgslice` command.
68
68
  pgslice fill <table> --swapped
69
69
  ```
70
70
 
71
- 8. Archive and drop the original table
71
+ 8. Back up the retired table with a tool like [pg_dump](https://www.postgresql.org/docs/current/static/app-pgdump.html) and drop it
72
+
73
+ ```sql
74
+ pg_dump -c -Fc -t <table>_retired $PGSLICE_URL > <table>_retired.dump
75
+ psql -c "DROP <table>_retired" $PGSLICE_URL
76
+ ```
72
77
 
73
78
  ## Sample Output
74
79
 
@@ -86,7 +91,7 @@ CREATE TABLE visits_intermediate (LIKE visits INCLUDING ALL);
86
91
  CREATE FUNCTION visits_insert_trigger()
87
92
  RETURNS trigger AS $$
88
93
  BEGIN
89
- RAISE EXCEPTION 'Date out of range. Create partitions first.';
94
+ RAISE EXCEPTION 'Create partitions first.';
90
95
  END;
91
96
  $$ LANGUAGE plpgsql;
92
97
 
@@ -155,18 +160,18 @@ pgslice fill visits
155
160
 
156
161
  ```sql
157
162
  /* 1 of 3 */
158
- INSERT INTO visits_intermediate (id, user_id, ip, created_at)
159
- SELECT id, user_id, ip, created_at FROM visits
163
+ INSERT INTO visits_intermediate ("id", "user_id", "ip", "created_at")
164
+ SELECT "id", "user_id", "ip", "created_at" FROM visits
160
165
  WHERE id > 0 AND id <= 10000 AND created_at >= '2016-08-01'::date AND created_at < '2016-11-01'::date
161
166
 
162
167
  /* 2 of 3 */
163
- INSERT INTO visits_intermediate (id, user_id, ip, created_at)
164
- SELECT id, user_id, ip, created_at FROM visits
168
+ INSERT INTO visits_intermediate ("id", "user_id", "ip", "created_at")
169
+ SELECT "id", "user_id", "ip", "created_at" FROM visits
165
170
  WHERE id > 10000 AND id <= 20000 AND created_at >= '2016-08-01'::date AND created_at < '2016-11-01'::date
166
171
 
167
172
  /* 3 of 3 */
168
- INSERT INTO visits_intermediate (id, user_id, ip, created_at)
169
- SELECT id, user_id, ip, created_at FROM visits
173
+ INSERT INTO visits_intermediate ("id", "user_id", "ip", "created_at")
174
+ SELECT "id", "user_id", "ip", "created_at" FROM visits
170
175
  WHERE id > 20000 AND id <= 30000 AND created_at >= '2016-08-01'::date AND created_at < '2016-11-01'::date
171
176
  ```
172
177
 
@@ -218,6 +223,21 @@ WHERE
218
223
  -- for months, use to_char(NOW() + INTERVAL '3 months', 'YYYYMM')
219
224
  ```
220
225
 
226
+ ## Archiving Partitions
227
+
228
+ Back up and drop older partitions each day or month.
229
+
230
+ ```sh
231
+ pg_dump -c -Fc -t <table>_201609 $PGSLICE_URL > <table>_201609.dump
232
+ psql -c "DROP <table>_201609" $PGSLICE_URL
233
+ ```
234
+
235
+ If you use [Amazon S3](https://aws.amazon.com/s3/) for backups, [s3cmd](https://github.com/s3tools/s3cmd) is a nice tool.
236
+
237
+ ```sh
238
+ s3cmd put <table>_201609.dump s3://<s3-bucket>/<table>_201609.dump
239
+ ```
240
+
221
241
  ## Additional Commands
222
242
 
223
243
  To undo prep (which will delete partitions), use:
@@ -268,7 +288,7 @@ You can also use pgslice to reduce the size of a table without partitioning by c
268
288
 
269
289
  ```sh
270
290
  pgslice prep <table> --no-partition
271
- pgslice fill <table> --start 1000 # starting primary key
291
+ pgslice fill <table> --where "id > 1000" # use any conditions
272
292
  pgslice swap <table>
273
293
  ```
274
294
 
@@ -79,7 +79,7 @@ CREATE TABLE #{intermediate_table} (LIKE #{table} INCLUDING ALL);
79
79
  CREATE FUNCTION #{trigger_name}()
80
80
  RETURNS trigger AS $$
81
81
  BEGIN
82
- RAISE EXCEPTION 'Date out of range. Create partitions first.';
82
+ RAISE EXCEPTION 'Create partitions first.';
83
83
  END;
84
84
  $$ LANGUAGE plpgsql;
85
85
  SQL
@@ -133,16 +133,13 @@ SQL
133
133
 
134
134
  queries = []
135
135
 
136
- comment = execute("SELECT obj_description(oid, 'pg_trigger') AS comment FROM pg_trigger WHERE tgname = $1 AND tgrelid = $2::regclass", [trigger_name, table]).first
137
- if comment
138
- field, period = comment["comment"].split(",").map { |v| v.split(":").last } rescue [nil, nil]
139
- end
136
+ period, field, needs_comment = settings_from_trigger(original_table, table)
137
+ abort "Could not read settings" unless period
140
138
 
141
- unless period
142
- period, field = settings_from_table(original_table, table)
139
+ if needs_comment
143
140
  queries << "COMMENT ON TRIGGER #{trigger_name} ON #{table} is 'column:#{field},period:#{period}';"
144
141
  end
145
- abort "Could not read settings" unless period
142
+
146
143
  # today = utc date
147
144
  today = round_date(DateTime.now.new_offset(0).to_date, period)
148
145
  added_partitions = []
@@ -229,7 +226,7 @@ CREATE OR REPLACE FUNCTION #{trigger_name}()
229
226
  abort "Table not found: #{source_table}" unless table_exists?(source_table)
230
227
  abort "Table not found: #{dest_table}" unless table_exists?(dest_table)
231
228
 
232
- period, field = settings_from_table(table, dest_table)
229
+ period, field, needs_comment = settings_from_trigger(table, dest_table)
233
230
 
234
231
  if period
235
232
  name_format = self.name_format(period)
@@ -258,7 +255,7 @@ CREATE OR REPLACE FUNCTION #{trigger_name}()
258
255
  end
259
256
 
260
257
  starting_id = max_dest_id
261
- fields = columns(source_table).join(", ")
258
+ fields = columns(source_table).map { |c| PG::Connection.quote_ident(c) }.join(", ")
262
259
  batch_size = options[:batch_size]
263
260
 
264
261
  i = 1
@@ -268,6 +265,9 @@ CREATE OR REPLACE FUNCTION #{trigger_name}()
268
265
  if period
269
266
  where << " AND #{field} >= #{sql_date(starting_time)} AND #{field} < #{sql_date(ending_time)}"
270
267
  end
268
+ if options[:where]
269
+ where << " AND #{options[:where]}"
270
+ end
271
271
 
272
272
  query = <<-SQL
273
273
  /* #{i} of #{batch_count} */
@@ -348,6 +348,7 @@ INSERT INTO #{dest_table} (#{fields})
348
348
  o.integer "--start"
349
349
  o.string "--url"
350
350
  o.string "--source-table"
351
+ o.string "--where"
351
352
  o.on "-v", "--version", "print the version" do
352
353
  log PgSlice::VERSION
353
354
  @exit = true
@@ -527,16 +528,27 @@ INSERT INTO #{dest_table} (#{fields})
527
528
  end
528
529
  end
529
530
 
530
- def settings_from_table(original_table, table)
531
+ def settings_from_trigger(original_table, table)
531
532
  trigger_name = self.trigger_name(original_table)
532
- function_def = execute("select pg_get_functiondef(oid) from pg_proc where proname = $1", [trigger_name])[0]
533
- return [nil, nil] unless function_def
534
- function_def = function_def["pg_get_functiondef"]
535
- sql_format = SQL_FORMAT.find { |_, f| function_def.include?("'#{f}'") }
536
- return [nil, nil] unless sql_format
537
- period = sql_format[0]
538
- field = /to_char\(NEW\.(\w+),/.match(function_def)[1]
539
- [period, field]
533
+
534
+ needs_comment = false
535
+ comment = execute("SELECT obj_description(oid, 'pg_trigger') AS comment FROM pg_trigger WHERE tgname = $1 AND tgrelid = $2::regclass", [trigger_name, table])[0]
536
+ if comment
537
+ field, period = comment["comment"].split(",").map { |v| v.split(":").last } rescue [nil, nil]
538
+ end
539
+
540
+ unless period
541
+ needs_comment = true
542
+ function_def = execute("select pg_get_functiondef(oid) from pg_proc where proname = $1", [trigger_name])[0]
543
+ return [nil, nil] unless function_def
544
+ function_def = function_def["pg_get_functiondef"]
545
+ sql_format = SQL_FORMAT.find { |_, f| function_def.include?("'#{f}'") }
546
+ return [nil, nil] unless sql_format
547
+ period = sql_format[0]
548
+ field = /to_char\(NEW\.(\w+),/.match(function_def)[1]
549
+ end
550
+
551
+ [period, field, needs_comment]
540
552
  end
541
553
  end
542
554
  end
@@ -1,3 +1,3 @@
1
1
  module PgSlice
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgslice
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-23 00:00:00.000000000 Z
11
+ date: 2016-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: slop