repertoire-repertoire-devtools 0.0.1 → 0.0.2
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.
- data/README.txt +12 -0
- data/bin/postgresql_crontab_sweep +96 -53
- metadata +4 -4
data/README.txt
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
This gem contains tools for use in configuring Hyperstudio servers and workstations. Currently it contains:
|
2
|
+
|
3
|
+
Process management tools:
|
4
|
+
|
5
|
+
- merb_persist: adaptation of Apple's mongrel_rails_persist launchd helper, for merb
|
6
|
+
- postgresql_crontab_sweep: crontab like tasks for postgresql (esp. for full text indexing)
|
7
|
+
|
8
|
+
All are ruby applications; see individual files for more information. To install:
|
9
|
+
|
10
|
+
sudo gem install repertoire-repertoire-devtools --source http://gems.github.com
|
11
|
+
|
12
|
+
[ repetition in the gem name is not a typo - it's a side effect of github's gem naming system ]
|
@@ -5,6 +5,8 @@
|
|
5
5
|
require "rubygems"
|
6
6
|
require "pg"
|
7
7
|
|
8
|
+
require 'benchmark'
|
9
|
+
|
8
10
|
#
|
9
11
|
# Basic support for crontab-type tasks within PostgreSQL
|
10
12
|
#
|
@@ -19,33 +21,36 @@ require "pg"
|
|
19
21
|
# Before running, create the crontab table as follows:
|
20
22
|
#
|
21
23
|
# CREATE TABLE crontab(id SERIAL PRIMARY KEY,
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
24
|
+
# role TEXT,
|
25
|
+
# notice TEXT NOT NULL,
|
26
|
+
# task TEXT NOT NULL,
|
27
|
+
# interval INTERVAL NOT NULL,
|
28
|
+
# last_updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT 'epoch',
|
29
|
+
# time_elapsed INTERVAL,
|
30
|
+
# failing BOOLEAN NOT NULL DEFAULT false,
|
31
|
+
# message TEXT);
|
28
32
|
#
|
29
33
|
# This script makes a single sweep through the crontab list, executing pending tasks. To use,
|
30
34
|
# run as a cron script (standard UNIX), or launchd task (OS X).
|
31
35
|
#
|
32
|
-
# If you would like the script to set up the database table and a
|
33
|
-
# the
|
34
|
-
# `sudo postgresql_crontab_sweep install <database-name>`
|
35
|
-
# on the command line (OS X only). This will generate the relevant files, tables, and start the
|
36
|
-
# update polling process.
|
36
|
+
# If you would like the script to set up the database table and a OS X launchd task, you can use
|
37
|
+
# the 'install' shortcut. For more information, see the help page available at 'postgresql_crontab_sweep -h'
|
37
38
|
#
|
38
39
|
# Each task is executed in a separate transaction, so they fail or succeed separately. If the
|
39
40
|
# task commits, its timestamp is updated. If it aborts, last_updated_at will show the most
|
40
41
|
# recent successful run, and the failing and message fields will be set.
|
41
42
|
#
|
42
43
|
# Parameters: -d <database> -p <port> -h <host> -u <user> -p <password> [ all are optional ]
|
43
|
-
# or: install <database>
|
44
|
+
# or: install <database> [ -i <interval in seconds> ]
|
44
45
|
#
|
45
46
|
# Because this script should only be run locally to the database, it expects postgres to accept
|
46
47
|
# your user without a password prompt (see pg_hba.conf to configure). Usually this user will
|
47
|
-
# be 'postgres', the db admin role.
|
48
|
-
#
|
48
|
+
# be 'postgres', the db admin role.
|
49
|
+
#
|
50
|
+
# When writing tasks, keep in mind that this user will execute them, unless the 'role' column has been
|
51
|
+
# filled. In this case, a SET ROLE <role> command will execute first, which allows the task to be
|
52
|
+
# written as though the provided role were executing it (most importantly, you can assume user's schema
|
53
|
+
# as default. You will need to grant this role to the superuser before this feature can be used, however.
|
49
54
|
#
|
50
55
|
# Christopher York 12/8/2008
|
51
56
|
#
|
@@ -57,7 +62,7 @@ def run(dbparams)
|
|
57
62
|
forking = false
|
58
63
|
|
59
64
|
# get the pending database cron tasks
|
60
|
-
pending = cron_conn.exec("SELECT
|
65
|
+
pending = cron_conn.exec("SELECT * FROM crontab WHERE last_updated_at + interval < now();")
|
61
66
|
|
62
67
|
pending.each do |crontask|
|
63
68
|
# fork a sub-process for each task, with its own database connection
|
@@ -68,8 +73,12 @@ def run(dbparams)
|
|
68
73
|
# all postgresql cron tasks take place in their own transaction (which is the reason for this script - not possible in plpgsql)
|
69
74
|
task_conn.transaction do
|
70
75
|
puts crontask[:notice]
|
71
|
-
task_conn.exec("
|
72
|
-
|
76
|
+
task_conn.exec("SET ROLE #{ crontask['role'] }") if crontask['role']
|
77
|
+
time_elapsed = Benchmark.realtime do
|
78
|
+
task_conn.exec(crontask['task'])
|
79
|
+
end
|
80
|
+
task_conn.exec("RESET ROLE")
|
81
|
+
task_conn.exec("UPDATE crontab SET last_updated_at = now(), time_elapsed = '#{ time_elapsed }', failing = false, message = NULL WHERE id = #{ crontask['id'] }")
|
73
82
|
end
|
74
83
|
rescue PGError => e
|
75
84
|
STDERR.puts "ERROR! #{ Time.now } - #{ e.message }"
|
@@ -84,50 +93,74 @@ def run(dbparams)
|
|
84
93
|
Process.wait if forking # avoid zombies by waiting for child processes to finish
|
85
94
|
end
|
86
95
|
|
87
|
-
def install(dbparams)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
<dict>
|
93
|
-
<key>Label</key>
|
94
|
-
<string>edu.mit.hyperstudio.postgresql.crontab.plist</string>
|
95
|
-
<key>OnDemand</key>
|
96
|
-
<false/>
|
97
|
-
<key>ProgramArguments</key>
|
98
|
-
<array>
|
99
|
-
<string>/usr/bin/postgresql_crontab_sweep</string>
|
100
|
-
<string>-d</string>
|
101
|
-
<string>#{dbparams['dbname']}</string>
|
102
|
-
</array>
|
103
|
-
<key>StartCalendarInterval</key>
|
104
|
-
<dict>
|
105
|
-
<key>StartInterval</key>
|
106
|
-
<integer>5</integer>
|
107
|
-
</dict>
|
108
|
-
</dict>
|
109
|
-
</plist>
|
110
|
-
END_OF_PLIST
|
111
|
-
|
112
|
-
create_table = <<-END_OF_CREATE_TABLE
|
96
|
+
def install(dbparams, script_args, sweep_interval)
|
97
|
+
require 'osx/cocoa'
|
98
|
+
include OSX
|
99
|
+
|
100
|
+
create_table_stmt = <<-END_OF_CREATE_TABLE
|
113
101
|
CREATE TABLE crontab(id SERIAL PRIMARY KEY,
|
114
102
|
notice TEXT NOT NULL,
|
103
|
+
role TEXT,
|
115
104
|
task TEXT NOT NULL,
|
116
105
|
interval INTERVAL NOT NULL,
|
117
106
|
last_updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT 'epoch',
|
107
|
+
time_elapsed INTERVAL,
|
118
108
|
failing BOOLEAN NOT NULL DEFAULT false,
|
119
109
|
message TEXT);
|
120
110
|
END_OF_CREATE_TABLE
|
121
111
|
|
122
|
-
|
123
|
-
|
112
|
+
# create database crontab table
|
113
|
+
puts "Creating crontab table in database #{ dbparams[:dbname] }"
|
124
114
|
conn = PGconn.open(dbparams)
|
125
|
-
|
115
|
+
begin
|
116
|
+
conn.exec(create_table_stmt)
|
117
|
+
rescue PGError => e
|
118
|
+
STDERR.puts "WARNING: #{ Time.now } - #{ e.message }"
|
119
|
+
end
|
126
120
|
conn.close
|
121
|
+
|
122
|
+
# create and install the crontab launchd task
|
123
|
+
script_path = File.expand_path(File.dirname(__FILE__) + '/' + File.basename(__FILE__))
|
124
|
+
plist_label = "edu.mit.hyperstudio.postgresql.crontab"
|
125
|
+
plist_path = "/Library/LaunchDaemons/#{plist_label}.plist"
|
126
|
+
|
127
|
+
if ! test(?d,File.dirname(plist_path))
|
128
|
+
raise unless system('/bin/mkdir', '-m', '0755', plist_path)
|
129
|
+
raise unless system('/usr/sbin/chown', 'root:wheel', plist_path)
|
130
|
+
end
|
131
|
+
|
132
|
+
h = {}
|
133
|
+
h['Label'] = plist_label
|
134
|
+
h['OnDemand'] = false
|
135
|
+
h['ProgramArguments'] = [script_path, script_args.to_a].flatten.compact
|
136
|
+
h['StartCalendarInterval'] = { 'StartInterval' => sweep_interval }
|
137
|
+
|
138
|
+
data = NSPropertyListSerialization.dataFromPropertyList_format_errorDescription(h, NSPropertyListXMLFormat_v1_0, nil)
|
139
|
+
plist = NSString.alloc.initWithData_encoding(data, NSUTF8StringEncoding)
|
140
|
+
File.open(plist_path, 'w') { |f| f.puts plist }
|
141
|
+
|
142
|
+
`/bin/launchctl unload #{plist_path} >/dev/null 2>&1`
|
143
|
+
raise unless system('/bin/launchctl', 'load', plist_path)
|
144
|
+
puts "Loaded #{plist_path}\nPostgreSQL crontab sweeper should persist across reboot."
|
145
|
+
puts "To stop: /bin/launchctl unload -w #{plist_path}"
|
146
|
+
|
147
|
+
exit
|
127
148
|
end
|
128
149
|
|
129
150
|
|
130
|
-
# parse params, with defaults
|
151
|
+
# parse params, with defaults; handle usage
|
152
|
+
|
153
|
+
usage = <<EOU
|
154
|
+
Usage: #{File.basename($0)} -h | [-d <database>] [-p <port> ] [-u <user>] [-P <password>] [install]
|
155
|
+
This is a sweep script for Repertoire's PostgreSQL crontab support. Run once to sweep through the crontab table,
|
156
|
+
executing each task in a separate transaction/process. Or run with the 'install' parameter to create the
|
157
|
+
crontab table and start periodic launchd task to run it. See in-script comments for more details.
|
158
|
+
EOU
|
159
|
+
|
160
|
+
if ARGV.empty? || ARGV.include?('-h')
|
161
|
+
$stderr.puts usage
|
162
|
+
exit
|
163
|
+
end
|
131
164
|
|
132
165
|
opts = {
|
133
166
|
:dbname => 'hyperstudio_development',
|
@@ -135,16 +168,26 @@ opts = {
|
|
135
168
|
:port => 5432,
|
136
169
|
:host => 'localhost'
|
137
170
|
}
|
171
|
+
args = {}
|
172
|
+
installing = false
|
173
|
+
sweep_interval = 5
|
138
174
|
|
139
175
|
while opt = ARGV.shift
|
140
176
|
case opt
|
141
|
-
when '-d' then opts[
|
142
|
-
when '-p' then opts[
|
143
|
-
when '-u' then opts[
|
144
|
-
when '-
|
145
|
-
when 'install' then
|
177
|
+
when '-d' then opts[:dbname] = args['-d'] = ARGV.shift
|
178
|
+
when '-p' then opts[:port] = args['-p'] = ARGV.shift
|
179
|
+
when '-u' then opts[:user] = args['-u'] = ARGV.shift
|
180
|
+
when '-P' then opts[:password] = args['-P'] = ARGV.shift
|
181
|
+
when 'install' then installing = true
|
182
|
+
when '-i' then sweep_interval = ARGV.shift
|
183
|
+
else raise "Unkown argument #{opt}"
|
146
184
|
end
|
147
185
|
end
|
148
186
|
|
149
|
-
|
187
|
+
if installing
|
188
|
+
install(opts, args, sweep_interval)
|
189
|
+
else
|
190
|
+
run(opts)
|
191
|
+
end
|
192
|
+
|
150
193
|
exit
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: repertoire-repertoire-devtools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christopher York
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-01-02 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -17,9 +17,9 @@ dependencies:
|
|
17
17
|
version_requirement:
|
18
18
|
version_requirements: !ruby/object:Gem::Requirement
|
19
19
|
requirements:
|
20
|
-
- - "
|
20
|
+
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0
|
22
|
+
version: "0"
|
23
23
|
version:
|
24
24
|
description: Development and server tools for use with Hyperstudio Repertoire
|
25
25
|
email: yorkc@mit.edu
|