thinner 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.
@@ -0,0 +1,121 @@
1
+ pre.dawn .MetaSeparator {
2
+ font-weight: bold;
3
+ background-color: #DCDCDC;
4
+ color: #19356D;
5
+ }
6
+ pre.dawn .SupportVariable {
7
+ color: #234A97;
8
+ }
9
+ pre.dawn .Constant {
10
+ font-weight: bold;
11
+ color: #811F24;
12
+ }
13
+ pre.dawn .EmbeddedSource {
14
+ background-color: #829AC2;
15
+ }
16
+ pre.dawn .StringRegexpConstantCharacterEscape {
17
+ font-weight: bold;
18
+ color: #811F24;
19
+ }
20
+ pre.dawn .Support {
21
+ color: #691C97;
22
+ }
23
+ pre.dawn .MarkupList {
24
+ color: #693A17;
25
+ }
26
+ pre.dawn .Storage {
27
+ color: #A71D5D;
28
+ font-style: italic;
29
+ }
30
+ pre.dawn .line-numbers {
31
+ background-color: #7496CF;
32
+ color: #000000;
33
+ }
34
+ pre.dawn .StringConstant {
35
+ font-weight: bold;
36
+ color: #696969;
37
+ }
38
+ pre.dawn .MarkupUnderline {
39
+ text-decoration: underline;
40
+ color: #080808;
41
+ }
42
+ pre.dawn .MarkupHeading {
43
+ font-weight: bold;
44
+ color: #19356D;
45
+ }
46
+ pre.dawn .SupportConstant {
47
+ color: #B4371F;
48
+ }
49
+ pre.dawn .MarkupQuote {
50
+ background-color: #C5C5C5;
51
+ color: #0B6125;
52
+ font-style: italic;
53
+ }
54
+ pre.dawn .StringRegexpSpecial {
55
+ font-weight: bold;
56
+ color: #CF5628;
57
+ }
58
+ pre.dawn .InvalidIllegal {
59
+ background-color: #B52A1D;
60
+ color: #F8F8F8;
61
+ font-style: italic;
62
+ }
63
+ pre.dawn .MarkupDeleted {
64
+ color: #B52A1D;
65
+ }
66
+ pre.dawn .MarkupRaw {
67
+ background-color: #C5C5C5;
68
+ color: #234A97;
69
+ }
70
+ pre.dawn .SupportFunction {
71
+ color: #693A17;
72
+ }
73
+ pre.dawn .PunctuationSeparator {
74
+ color: #794938;
75
+ }
76
+ pre.dawn .StringRegexp {
77
+ color: #CF5628;
78
+ }
79
+ pre.dawn .StringEmbeddedSource {
80
+ background-color: #829AC2;
81
+ color: #080808;
82
+ }
83
+ pre.dawn .MarkupLink {
84
+ color: #234A97;
85
+ font-style: italic;
86
+ }
87
+ pre.dawn .MarkupBold {
88
+ font-weight: bold;
89
+ color: #080808;
90
+ }
91
+ pre.dawn .StringVariable {
92
+ color: #234A97;
93
+ }
94
+ pre.dawn .String {
95
+ color: #0B6125;
96
+ }
97
+ pre.dawn .Keyword {
98
+ color: #794938;
99
+ }
100
+ pre.dawn {
101
+ background-color: #F5F5F5;
102
+ color: #080808;
103
+ }
104
+ pre.dawn .MarkupItalic {
105
+ color: #080808;
106
+ font-style: italic;
107
+ }
108
+ pre.dawn .InvalidDeprecated {
109
+ font-weight: bold;
110
+ color: #B52A1D;
111
+ }
112
+ pre.dawn .Variable {
113
+ color: #234A97;
114
+ }
115
+ pre.dawn .Entity {
116
+ color: #BF4F24;
117
+ }
118
+ pre.dawn .Comment {
119
+ color: #5A525F;
120
+ font-style: italic;
121
+ }
@@ -0,0 +1,53 @@
1
+ body {
2
+ font-family: Garamond, Baskerville, "Baskerville Old Face", "Hoefler Text", "Times New Roman", serif;
3
+ font-size: 16px;
4
+ line-height:20px;
5
+ width: 600px;
6
+ margin-left:auto;
7
+ margin-right:auto;
8
+ background: #f4f4f4;
9
+ }
10
+ a.propublica{
11
+ position:absolute;
12
+ background: transparent url(../images/proplogo.png) no-repeat -40px -20px;
13
+ top: 0;
14
+ left: 0;
15
+ width: 160px;
16
+ height: 141px;
17
+ }
18
+
19
+ pre {
20
+ font-family: Monaco, Courier, monospace;
21
+ font-size: 12px;
22
+ line-height: 16px;
23
+ padding:0.5em 1em;
24
+ overflow: auto;
25
+ border-left: 4px solid #143D8D;
26
+ margin-left: 1em;
27
+ }
28
+ a {
29
+ color: #143D8D;
30
+ text-decoration: none;
31
+ font-weight: bold;
32
+ }
33
+ ul {
34
+ margin:0 1em;
35
+ padding:0;
36
+ list-style: none;
37
+ }
38
+ li {
39
+ margin:0;
40
+ padding:0;
41
+ }
42
+ strong {
43
+ font-family: Monaco, Courier, monospace;
44
+ font-weight: normal;
45
+ background: #dadee5;
46
+ border: 1px solid #aaa;
47
+ padding: 1px 2px;
48
+ font-size: 12px;
49
+ }
50
+ p{ margin: 0 0 1em 0 }
51
+ h3{
52
+ margin-bottom: 0px;
53
+ }
@@ -0,0 +1,25 @@
1
+ Thinner.configure do |config|
2
+ # Number of urls to purge at one time. These purge requests are fired in quick
3
+ # succession. Thinner is perfectly capable of killing a Varnish server, by
4
+ # overloading the worker thread, so be really conservative with this option.
5
+ config.batch_length = 10
6
+
7
+ # The amount of time to sleep between purges in seconds.
8
+ config.sleep_time = 1
9
+
10
+ # The server address and management port. See:
11
+ # http://www.varnish-cache.org/trac/wiki/ManagementPort
12
+ # for details.
13
+ config.server = "127.0.0.1:6082"
14
+
15
+ # By default, every time you call Thinner.purge! thinner spins off a new
16
+ # instance of Thinner::Client and terminates any old instances that are
17
+ # running. If you want to have overlapping instances set this to true.
18
+ # It's not recommended to have multiple Thinner::Client's running at the
19
+ # same time.
20
+ config.no_kill = false
21
+
22
+ # The log file (either a string or file object) to log the current batch to.
23
+ # Defaults to STDOUT
24
+ config.log_file = STDOUT
25
+ end
@@ -0,0 +1,5 @@
1
+ # The urls in this array are purged in order, so you'll want to structure it
2
+ # according to usage.
3
+ arr << ["/some_route", "/"]
4
+
5
+ Thinner.purge! arr
Binary file
@@ -0,0 +1,64 @@
1
+ <%
2
+ $:.unshift File.expand_path(File.dirname(__FILE__), "/../lib/thinner")
3
+ DOCS = "documentation/examples/"
4
+ require 'uv'
5
+ def code_for(file)
6
+ return '' unless File.exists?("#{DOCS}#{file}.rb")
7
+ file = File.open("#{DOCS}#{file}.rb").read
8
+ Uv.parse(file, "xhtml", "ruby", false, "dawn", false)
9
+ end
10
+ %>
11
+ <!DOCTYPE html>
12
+ <html>
13
+ <head>
14
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
15
+ <title>Thinner -- Version <%= File.read('VERSION') %></title>
16
+ <link rel="stylesheet" type="text/css" href="documentation/css/styles.css" />
17
+ <link rel="stylesheet" type="text/css" href="documentation/css/dawn.css" />
18
+ </head>
19
+ <body>
20
+ <a href="http://www.propublica.org" class="propublica">&nbsp;</a>
21
+ <h1>Thinner &ndash; Version <%= File.read('VERSION') %></h1>
22
+ <p><a href="https://github.com/propublica/Thinner">Thinner</a> is a utility
23
+ for purging urls from a Varnish server.</p>
24
+ <p>When you are deploying code changes to a server under load, the uncached
25
+ load can quickly bring down your backend server even with Varnish's grace
26
+ mode. Often, allowing stale caches to stick around for a while saves
27
+ both server performance and sanity.</p>
28
+ <p>Thinner gives you fine-grained control over wildcard purging, and rolls
29
+ purges out slowly. Your users will see stale pages from the previous deploy
30
+ until Thinner has finished invalidating the stale cache at a rate that you set.
31
+ If you have a bunch of pages you need to invalidate en masse, but don't
32
+ want to risk overloading your server, Thinner is for you.</p>
33
+ <p>All that being said, Thinner isn't really a solution for observing
34
+ model changes and purging associated urls. If you have a highly dynamic
35
+ application, it's worlds better to handle purging via a
36
+ <a href="http://github.com/russ/lacquer/blob/master/lib/lacquer/delayed_job_job.rb">job server</a>
37
+ outside of the request-response flow.</p>
38
+ <p><a href="doc/index.html">API docs</a> | <a href="http://github.com/propublica/thinner/issues">Issue Tracker</a></p>
39
+ <h2>Installation</h2>
40
+ <p>Thinner is available via rubygems:
41
+ <pre>gem install thinner</pre>
42
+ <h2>Usage</h2>
43
+ <p>Thinner has both a library and command-line interface. To use it as a gem
44
+ you'll first have to configure how it works by calling <strong>Thinner.configure</strong>.
45
+ Here's a quick rundown of all of the options available:</p>
46
+ <%= code_for "configure" %>
47
+ <p>Once you have the configuration in place call <strong>purge!</strong> with
48
+ an array of urls:</p>
49
+ <%= code_for "purge" %>
50
+ <p>Thinner will then fork a background process and purge the urls. You can
51
+ check the progress of the purge by tailing the log file or with:</p>
52
+ <pre>varnishlog | grep purge</pre>
53
+ <p>If ruby's not your cup of tea, Thinner also has a command line interface.
54
+ Once you've installed the gem run <strong>thinner -h</strong> to see the
55
+ available options.</p>
56
+ <p>The command line interface accepts a newline separated list of urls via
57
+ stdin by setting the <strong>-e</strong> flag. So you'll be able to use
58
+ the command like so:</p>
59
+ <pre>cat urls_to_purge.txt | bin/thinner -e</pre>
60
+ <h2>Change Log</h2>
61
+ <h3>0.1.0</h3>
62
+ <p>Initial release.</p>
63
+ </body>
64
+ </html>
data/index.html ADDED
@@ -0,0 +1,87 @@
1
+
2
+ <!DOCTYPE html>
3
+ <html>
4
+ <head>
5
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
6
+ <title>Thinner -- Version 0.1.0
7
+ </title>
8
+ <link rel="stylesheet" type="text/css" href="documentation/css/styles.css" />
9
+ <link rel="stylesheet" type="text/css" href="documentation/css/dawn.css" />
10
+ </head>
11
+ <body>
12
+ <a href="http://www.propublica.org" class="propublica">&nbsp;</a>
13
+ <h1>Thinner &ndash; Version 0.1.0
14
+ </h1>
15
+ <p><a href="https://github.com/propublica/Thinner">Thinner</a> is a utility
16
+ for purging urls from a Varnish server.</p>
17
+ <p>When you are deploying code changes to a server under load, the uncached
18
+ load can quickly bring down your backend server even with Varnish's grace
19
+ mode. Often, allowing stale caches to stick around for a while saves
20
+ both server performance and sanity.</p>
21
+ <p>Thinner gives you fine-grained control over wildcard purging, and rolls
22
+ purges out slowly. Your users will see stale pages from the previous deploy
23
+ until Thinner has finished invalidating the stale cache at a rate that you set.
24
+ If you have a bunch of pages you need to invalidate en masse, but don't
25
+ want to risk overloading your server, Thinner is for you.</p>
26
+ <p>All that being said, Thinner isn't really a solution for observing
27
+ model changes and purging associated urls. If you have a highly dynamic
28
+ application, it's worlds better to handle purging via a
29
+ <a href="http://github.com/russ/lacquer/blob/master/lib/lacquer/delayed_job_job.rb">job server</a>
30
+ outside of the request-response flow.</p>
31
+ <p><a href="doc/index.html">API docs</a> | <a href="http://github.com/propublica/thinner/issues">Issue Tracker</a></p>
32
+ <h2>Installation</h2>
33
+ <p>Thinner is available via rubygems:
34
+ <pre>gem install thinner</pre>
35
+ <h2>Usage</h2>
36
+ <p>Thinner has both a library and command-line interface. To use it as a gem
37
+ you'll first have to configure how it works by calling <strong>Thinner.configure</strong>.
38
+ Here's a quick rundown of all of the options available:</p>
39
+ <pre class="dawn"><span class="Support">Thinner</span><span class="PunctuationSeparator">.</span><span class="Entity">configure</span> <span class="Keyword">do </span><span class="PunctuationSeparator">|</span><span class="Variable">config</span><span class="PunctuationSeparator">|</span>
40
+ <span class="Comment"> <span class="Comment">#</span> Number of urls to purge at one time. These purge requests are fired in quick</span>
41
+ <span class="Comment"> <span class="Comment">#</span> succession. Thinner is perfectly capable of killing a Varnish server, by</span>
42
+ <span class="Comment"> <span class="Comment">#</span> overloading the worker thread, so be really conservative with this option.</span>
43
+ config<span class="PunctuationSeparator">.</span><span class="Entity">batch_length</span> <span class="Keyword">=</span> <span class="Constant">10</span>
44
+
45
+ <span class="Comment"> <span class="Comment">#</span> The amount of time to sleep between purges in seconds.</span>
46
+ config<span class="PunctuationSeparator">.</span><span class="Entity">sleep_time</span> <span class="Keyword">=</span> <span class="Constant">1</span>
47
+
48
+ <span class="Comment"> <span class="Comment">#</span> The server address and management port. See:</span>
49
+ <span class="Comment"> <span class="Comment">#</span> http://www.varnish-cache.org/trac/wiki/ManagementPort</span>
50
+ <span class="Comment"> <span class="Comment">#</span> for details.</span>
51
+ config<span class="PunctuationSeparator">.</span><span class="Entity">server</span> <span class="Keyword">=</span> &quot;127.0.0.1:6082&quot;
52
+
53
+ <span class="Comment"> <span class="Comment">#</span> By default, every time you call Thinner.purge! thinner spins off a new</span>
54
+ <span class="Comment"> <span class="Comment">#</span> instance of Thinner::Client and terminates any old instances that are</span>
55
+ <span class="Comment"> <span class="Comment">#</span> running. If you want to have overlapping instances set this to true.</span>
56
+ <span class="Comment"> <span class="Comment">#</span> It's not recommended to have multiple Thinner::Client's running at the</span>
57
+ <span class="Comment"> <span class="Comment">#</span> same time.</span>
58
+ config<span class="PunctuationSeparator">.</span><span class="Entity">no_kill</span> <span class="Keyword">=</span> <span class="Constant">false</span>
59
+
60
+ <span class="Comment"> <span class="Comment">#</span> The log file (either a string or file object) to log the current batch to.</span>
61
+ <span class="Comment"> <span class="Comment">#</span> Defaults to STDOUT</span>
62
+ config<span class="PunctuationSeparator">.</span><span class="Entity">log_file</span> <span class="Keyword">=</span> <span class="Variable">STDOUT</span>
63
+ <span class="Keyword">end</span>
64
+ </pre>
65
+ <p>Once you have the configuration in place call <strong>purge!</strong> with
66
+ an array of urls:</p>
67
+ <pre class="dawn"><span class="Comment"><span class="Comment">#</span> The urls in this array are purged in order, so you'll want to structure it</span>
68
+ <span class="Comment"><span class="Comment">#</span> according to usage.</span>
69
+ arr <span class="Keyword">&lt;&lt;</span> [&quot;/some_route&quot;<span class="PunctuationSeparator">,</span> &quot;/&quot;]
70
+
71
+ <span class="Support">Thinner</span><span class="PunctuationSeparator">.</span><span class="Entity">purge!</span> arr
72
+ </pre>
73
+ <p>Thinner will then fork a background process and purge the urls. You can
74
+ check the progress of the purge by tailing the log file or with:</p>
75
+ <pre>varnishlog | grep purge</pre>
76
+ <p>If ruby's not your cup of tea, Thinner also has a command line interface.
77
+ Once you've installed the gem run <strong>thinner -h</strong> to see the
78
+ available options.</p>
79
+ <p>The command line interface accepts a newline separated list of urls via
80
+ stdin by setting the <strong>-e</strong> flag. So you'll be able to use
81
+ the command like so:</p>
82
+ <pre>cat urls_to_purge.txt | bin/thinner -e</pre>
83
+ <h2>Change Log</h2>
84
+ <h3>0.1.0</h3>
85
+ <p>Initial release.</p>
86
+ </body>
87
+ </html>
@@ -0,0 +1,88 @@
1
+ require 'logger'
2
+
3
+ module Thinner
4
+
5
+ # A Thinner::Client runs as a background process and purges a list of urls
6
+ # in batches.
7
+ class Client
8
+
9
+ # A list of successfully purged urls.
10
+ attr_reader :purged_urls
11
+
12
+ # The list of Errors we want to catch.
13
+ ERRORS = [Varnish::Error, Varnish::BrokenConnection, Varnish::CommandFailed, Timeout::Error, Errno::ECONNREFUSED]
14
+
15
+ # Before purging, each Thinner::Client grabs various configuration settings
16
+ # and makes a copy of the passed in urls.
17
+ def initialize(urls)
18
+ @batch = Thinner.configuration.batch_length
19
+ @timeout = Thinner.configuration.sleep_time
20
+ @varnish = Varnish::Client.new Thinner.configuration.server
21
+ @log_file = Thinner.configuration.log_file
22
+ @purged_urls = []
23
+ @urls = Array.new urls
24
+ @length = @urls.length
25
+ logger
26
+ handle_errors
27
+ end
28
+
29
+ # Kickstart the purging process and loop through the array until there aren't
30
+ # any urls left to purge. Each time the loop runs it will update the process
31
+ # label with the first url in the list.
32
+ def run!
33
+ while @urls.length > 0
34
+ @current_job = @urls.slice! 0, @batch
35
+ $0 = "#{PROCESS_IDENTIFIER}: purging #{@current_job.first}"
36
+ purge_urls
37
+ sleep @timeout
38
+ end
39
+ close_log
40
+ end
41
+
42
+ private
43
+
44
+ # Once a batch is ready the Client fires off purge requests on the list of
45
+ # urls.
46
+ def purge_urls
47
+ @current_job.each do |url|
48
+ begin
49
+ @varnish.start if @varnish.stopped?
50
+ while(!@varnish.running?) do sleep 0.1 end
51
+ if @varnish.purge :url, url
52
+ @logger.info "Purged url: #{url}"
53
+ @purged_urls << url
54
+ else
55
+ @logger.warn "Could not purge: #{url}"
56
+ end
57
+ rescue *ERRORS => e
58
+ @logger.warn "Error on url: #{url}, message: #{e}"
59
+ sleep @timeout
60
+ end
61
+ end
62
+ end
63
+
64
+ # Trap certain signals so the Client can report back the progress of the
65
+ # job and close the log.
66
+ def handle_errors
67
+ trap('TERM') { close_log }
68
+ trap('KILL') { close_log }
69
+ trap('INT') { close_log }
70
+ end
71
+
72
+ # The logger redirects all STDOUT writes to a logger instance.
73
+ def logger
74
+ if !@log_file.respond_to?(:write)
75
+ STDOUT.reopen(File.open(@log_file, (File::WRONLY | File::APPEND | File::CREAT)))
76
+ end
77
+ @logger = Logger.new(STDOUT)
78
+ end
79
+
80
+ # Log the purged urls and exit the process.
81
+ def close_log
82
+ @logger.info "Purged #{@purged_urls.length} of #{@length} urls."
83
+ @logger.info "Exiting..."
84
+ end
85
+
86
+ end
87
+
88
+ end
@@ -0,0 +1,80 @@
1
+ require 'optparse'
2
+ require File.expand_path("#{File.dirname __FILE__}/../thinner.rb")
3
+
4
+ module Thinner
5
+
6
+ class CommandLine
7
+
8
+ # Usage and summary
9
+ BANNER = <<-EOF
10
+ Thinner purges varnish caches as slowly as you need it to.
11
+
12
+ Documentation: http://propublica.github.com/thinner/
13
+
14
+ Usage: thinner OPTIONS URL
15
+
16
+ Options:
17
+ EOF
18
+ # Create a Thinner::CommandLine, parse any associated options, grab a list
19
+ # of urls and start the process
20
+ def initialize
21
+ @urls = []
22
+ options!
23
+ @urls ||= ARGV
24
+ run!
25
+ end
26
+
27
+ # Build a Thinner::Configuration instance from the passed in options and go
28
+ # to the races.
29
+ def run!
30
+ Thinner.configure do |config|
31
+ @options.each_pair do |key, value|
32
+ config.send("#{key}=".to_sym, value)
33
+ end
34
+ end
35
+ Thinner.purge! @urls
36
+ end
37
+
38
+ private
39
+
40
+ # Parse the command line options using OptionParser.
41
+ def options!
42
+ @options = {}
43
+ @option_parser = OptionParser.new(BANNER) do |opts|
44
+ opts.on("-b", "--batch_length BATCH", "Number of urls to purge at once") do |b|
45
+ @options[:batch_length] = b.to_i
46
+ end
47
+ opts.on("-t", "--sleep_time SLEEP", "Time to wait in between batches") do |t|
48
+ @options[:sleep_time] = t.to_i
49
+ end
50
+ opts.on("-e", "--stdin", "Use stdin for urls") do
51
+ ARGF.each_line do |url|
52
+ @urls << url.chomp
53
+ end
54
+ end
55
+ opts.on("-s", "--server SERVER", "Varnish url, e.g. 127.0.0.1:6082") do |s|
56
+ @options[:server] = s
57
+ end
58
+ opts.on("-o", "--log_file LOG_PATH", "Log file to output to (default: Standard Out") do |o|
59
+ @options[:log_file] = o
60
+ end
61
+ opts.on("-n", "--no-kill", "Don't kill the running purgers if they exist") do |n|
62
+ @options[:no_kill] = n
63
+ end
64
+ opts.on_tail("-h", "--help", "Display this help message") do
65
+ puts opts.help
66
+ exit
67
+ end
68
+ end
69
+
70
+ begin
71
+ @option_parser.parse!(ARGV)
72
+ rescue OptionParser::InvalidOption => e
73
+ puts e.message
74
+ exit(1)
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,37 @@
1
+ module Thinner
2
+
3
+ # Thinner::Configuration holds the various settings for Thinner
4
+ class Configuration
5
+
6
+ attr_accessor :batch_length, :sleep_time, :server, :log_file, :no_kill
7
+
8
+ # Create a Thinner::Configuration instance with sane defaults.
9
+ def initialize
10
+ # Number of urls to purge at one time. These purge requests are fired in quick
11
+ # succession. Thinner is perfectly capable of killing a Varnish server, by
12
+ # overloading the worker thread, so be really conservative with this option.
13
+ @batch_length = 10
14
+
15
+ # The amount of time to sleep between purges in seconds.
16
+ @sleep_time = 1
17
+
18
+ # The server address and management port. See:
19
+ # http://www.varnish-cache.org/trac/wiki/ManagementPort
20
+ # for details.
21
+ @server = "127.0.0.1:6082"
22
+
23
+ # By default, every time you call Thinner.purge! thinner spins off a new
24
+ # instance of Thinner::Client and terminates any old instances that are
25
+ # running. If you want to have overlapping instances set this to true.
26
+ # It's not recommended to have multiple Thinner::Client's running at the
27
+ # same time.
28
+ @no_kill = false
29
+
30
+ # The log file (either a string or file object) to log the current batch to.
31
+ # Defaults to STDOUT
32
+ @log_file = STDOUT
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,46 @@
1
+ module Thinner
2
+
3
+ # A Thinner::Purger dispatches a client and ensures only one instance of a
4
+ # Thinner::Client is running at a given time.
5
+ class Purger
6
+
7
+ # Each Purger accepts a list of urls to pass on to the client to purge.
8
+ def initialize(urls)
9
+ @urls = urls
10
+ end
11
+
12
+ # After the configuration is in place and the Purger has a list of urls,
13
+ # it can fork a client process to run in the background. By default the
14
+ # Purger will kill any old Thinner::Client processes still running so as
15
+ # to not double up on purge requests.
16
+ def purge!
17
+ self.class.stop! unless Thinner.configuration.no_kill
18
+ puts "==== Starting purge see: #{Thinner.configuration.log_file} for finished urls."
19
+ client_id = fork {
20
+ Client.new(@urls).run!
21
+ }
22
+ Process.detach(client_id)
23
+ end
24
+
25
+ # A list of Thinner::Client process ids -- adapted from resque.
26
+ def self.job_ids
27
+ lines = `ps -A -o pid,command | grep #{PROCESS_IDENTIFIER}`.split("\n").map do |line|
28
+ line.split(' ')[0].to_i
29
+ end
30
+ end
31
+
32
+ # Before we spin up a new client each running process is killed by pid. Each
33
+ # killed process's id is logged in the Thinner log file.
34
+ def self.stop!
35
+ job_ids.each do |pid|
36
+ begin
37
+ Process.kill("KILL", pid.to_i)
38
+ puts "==== Killing process: #{pid}"
39
+ rescue Errno::ESRCH
40
+ end
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end
data/lib/thinner.rb ADDED
@@ -0,0 +1,37 @@
1
+ module Thinner
2
+
3
+ # The base location of the Thinner gem.
4
+ ROOT = File.expand_path "#{File.dirname __FILE__}/.."
5
+
6
+ # The Thinner version.
7
+ VERSION = File.read("#{ROOT}/VERSION").chomp
8
+
9
+ # The process label to run each Thinner::Client under.
10
+ PROCESS_IDENTIFIER = "Thinner"
11
+
12
+ # Set up the configuration instance as a class level accessor.
13
+ class << self; attr_accessor :configuration; end
14
+
15
+ # Set any thinner settings by passing in a block.
16
+ def self.configure
17
+ self.configuration ||= Configuration.new
18
+ yield configuration
19
+ end
20
+
21
+ # Halt any running instances of Thinner::Client
22
+ def self.stop!
23
+ Purger.stop!
24
+ end
25
+
26
+ # Begin purging urls.
27
+ def self.purge! urls
28
+ Purger.new(urls).purge!
29
+ end
30
+
31
+ end
32
+
33
+ require "klarlack"
34
+ require "logger"
35
+ require "#{Thinner::ROOT}/lib/thinner/configuration"
36
+ require "#{Thinner::ROOT}/lib/thinner/client"
37
+ require "#{Thinner::ROOT}/lib/thinner/purger"
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'thinner'
8
+ Thinner.configure {}
9
+ URLS = ["/"]
10
+