syncache 1.0.0 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog.mtn ADDED
@@ -0,0 +1,183 @@
1
+ -----------------------------------------------------------------
2
+ Revision: 8bc8efa1310743cf39e14bdf69d4429cadebcb61
3
+ Ancestor: 6f9a39297f610f7095473ac34f68674963561d9c
4
+ Author: angdraug@debian.org
5
+ Date: 25/12/09 19:56:53
6
+ Branch: syncache-head
7
+
8
+ Modified files:
9
+ bin/syncache-drb man/syncache-drb.1
10
+
11
+ ChangeLog:
12
+
13
+ man page updated for new options
14
+
15
+ -----------------------------------------------------------------
16
+ Revision: 6f9a39297f610f7095473ac34f68674963561d9c
17
+ Ancestor: 57f2c6cd4fbe8a04750aebeff72b094dc4d24476
18
+ Author: angdraug@debian.org
19
+ Date: 25/12/09 19:46:42
20
+ Branch: syncache-head
21
+
22
+ Modified files:
23
+ bin/syncache-drb
24
+
25
+ ChangeLog:
26
+
27
+ New options: --user, --error-log
28
+
29
+ * Redirect STDERR to an error log when specified.
30
+ * Allow to specify any to run as user instead of just nobody.
31
+ * Create the pidfile under /var/run/syncache-drb to solve the
32
+ undeletable pidfile problem.
33
+
34
+ -----------------------------------------------------------------
35
+ Revision: 57f2c6cd4fbe8a04750aebeff72b094dc4d24476
36
+ Ancestor: 0b3852cfbf276589043a4bb87c37ab024df694cc
37
+ Author: angdraug@debian.org
38
+ Date: 26/10/09 20:08:45
39
+ Branch: syncache-head
40
+
41
+ Modified files:
42
+ bin/syncache-drb
43
+
44
+ ChangeLog:
45
+
46
+ untaint URI supplied on command line; report exception backtraces to syslog
47
+
48
+ -----------------------------------------------------------------
49
+ Revision: 0b3852cfbf276589043a4bb87c37ab024df694cc
50
+ Ancestor: 69d9bb2d250a211e52baab031e5f840032fdee74
51
+ Author: angdraug@debian.org
52
+ Date: 07/08/09 14:08:50
53
+ Branch: syncache-head
54
+
55
+ Modified files:
56
+ lib/syncache.rb lib/syncache_sync_patch.rb
57
+
58
+ ChangeLog:
59
+
60
+ expand syncache_sync_patch to fix the same bug in Ruby 1.9.0, move RUBY_VERSION condition into the patch file
61
+
62
+ -----------------------------------------------------------------
63
+ Revision: 69d9bb2d250a211e52baab031e5f840032fdee74
64
+ Ancestor: de7c67553a07c221dc2064cc19a64c32286c72ae
65
+ Author: angdraug@debian.org
66
+ Date: 28/07/09 17:45:55
67
+ Branch: syncache-head
68
+
69
+ Added files:
70
+ test/ts_syncache.rb
71
+ Added directories:
72
+ test
73
+
74
+ ChangeLog:
75
+
76
+ unit test re-added from Samizdat::Cache
77
+
78
+ -----------------------------------------------------------------
79
+ Revision: de7c67553a07c221dc2064cc19a64c32286c72ae
80
+ Ancestor: f95d143aed0ae773800a96cafb4e1b9eb87cc407
81
+ Author: angdraug@debian.org
82
+ Date: 28/07/09 17:29:16
83
+ Branch: syncache-head
84
+
85
+ Modified files:
86
+ bin/syncache-drb
87
+
88
+ ChangeLog:
89
+
90
+ fixes in handling of syncache-drb options
91
+
92
+ -----------------------------------------------------------------
93
+ Revision: f95d143aed0ae773800a96cafb4e1b9eb87cc407
94
+ Ancestor: ed2cc574a6324a95a69f81c4a3ed3e04bd26ade4
95
+ Author: angdraug@debian.org
96
+ Date: 27/07/09 17:50:10
97
+ Branch: syncache-head
98
+
99
+ Modified files:
100
+ README.rdoc
101
+
102
+ ChangeLog:
103
+
104
+ updated README.rdoc to reflect renamed DRb server script
105
+
106
+ -----------------------------------------------------------------
107
+ Revision: ed2cc574a6324a95a69f81c4a3ed3e04bd26ade4
108
+ Ancestor: e9c480978fdb812474c40a785d7b4cae8aa77c58
109
+ Author: angdraug@debian.org
110
+ Date: 27/07/09 17:32:17
111
+ Branch: syncache-head
112
+
113
+ Renamed entries:
114
+ man/syncache-drb-server.1 to man/syncache-drb.1
115
+ Modified files:
116
+ man/syncache-drb.1
117
+
118
+ ChangeLog:
119
+
120
+ renamed the samizdat-drb-server.1 manpage along with the script
121
+
122
+ -----------------------------------------------------------------
123
+ Revision: e9c480978fdb812474c40a785d7b4cae8aa77c58
124
+ Ancestor: e6c0480854ee7af7f7f9956d9c6489452ec99a65
125
+ Author: angdraug@debian.org
126
+ Date: 27/07/09 17:16:05
127
+ Branch: syncache-head
128
+
129
+ Renamed entries:
130
+ bin/syncache-drb-server to bin/syncache-drb
131
+ Modified files:
132
+ bin/syncache-drb
133
+
134
+ ChangeLog:
135
+
136
+ renamed syncache-drb-server to syncache-drb to make it fit within 15 character limit
137
+
138
+ -----------------------------------------------------------------
139
+ Revision: e6c0480854ee7af7f7f9956d9c6489452ec99a65
140
+ Ancestor: 6818ae15675fa101beb33c7fc21635e404a09fbe
141
+ Author: angdraug@debian.org
142
+ Date: 27/07/09 16:37:53
143
+ Branch: syncache-head
144
+
145
+ Modified files:
146
+ lib/syncache.rb
147
+
148
+ ChangeLog:
149
+
150
+ conditionally load monkey patch for the sync.rb bug only for Ruby versions < 1.8.7p173
151
+
152
+ -----------------------------------------------------------------
153
+ Revision: 6818ae15675fa101beb33c7fc21635e404a09fbe
154
+ Ancestor: 56a8b045280179815127c312f703c55bc63d7081
155
+ Author: angdraug@debian.org
156
+ Date: 27/07/09 15:57:09
157
+ Branch: syncache-head
158
+
159
+ Modified files:
160
+ bin/syncache-drb-server
161
+
162
+ ChangeLog:
163
+
164
+ fixed Samizdat legacy: cache -> @cache
165
+
166
+ -----------------------------------------------------------------
167
+ Revision: 56a8b045280179815127c312f703c55bc63d7081
168
+ Ancestor:
169
+ Author: angdraug@debian.org
170
+ Date: 27/07/09 15:40:15
171
+ Branch: syncache-head
172
+
173
+ Added files:
174
+ COPYING README.rdoc bin/syncache-drb-server lib/syncache.rb
175
+ lib/syncache_sync_patch.rb man/syncache-drb-server.1
176
+ setup.rb
177
+ Added directories:
178
+ . bin lib man
179
+
180
+ ChangeLog:
181
+
182
+ initial checkin: SynCache is a spin-off of cache.rb from Samizdat project
183
+
data/README.rdoc CHANGED
@@ -42,8 +42,8 @@ overriding its #replacement_index method.
42
42
  Cache entries are automatically invalidated when their +ttl+ (time to
43
43
  live) is exceeded. Entries can be explicitly invalidated by #flush
44
44
  method. The method can use <tt>===</tt> operator to compare cache keys
45
- against flush base (so that base can be e.g. a Regexp), and invalidates
46
- all entries when invoked without the +base+ parameter.
45
+ against flush base (so that base can be e.g. a Regexp). When invoked
46
+ without the +base+ parameter, it invalidates all entries.
47
47
 
48
48
  The +flush_delay+ initialization option allows to limit cache's flush
49
49
  rate. When this option is set, SynCache will make sure that at least
@@ -70,22 +70,29 @@ To access a remote cache, you will need to use DRb library:
70
70
  # allow remote cache to access local objects from fetch_or_add blocks
71
71
  DRb.start_service('druby://localhost:0')
72
72
 
73
+ For the above to work properly, all your fetch_or_add blocks should be
74
+ thread-safe, because DRb will run them in their own threads. Also, if a
75
+ Ruby process crashes is the middle of such block, the key will remain
76
+ locked in the remote cache until its +ttl+ runs out.
77
+
78
+ To work around these limitations, you can wrap access to a remote cache
79
+ using a SynCache::RemoteCache object:
80
+
81
+ require 'syncache'
82
+
83
+ # connect to the remote cache
84
+ @cache = SynCache::RemoteCache.new('druby://localhost:9000')
85
+
86
+ SynCache::RemoteCache implements its own version of fetch_or_add that
87
+ runs the supplied block locally in the current thread and would give up
88
+ and take over a locked key if the client that originally locked it takes
89
+ too long to free it up.
90
+
73
91
 
74
92
  == Copying
75
93
 
76
- Copyright (c) 2002-2009 Dmitry Borodaenko <angdraug@debian.org>
94
+ Copyright (c) 2002-2011 Dmitry Borodaenko <angdraug@debian.org>
77
95
 
78
96
  This program is free software.
79
97
  You can distribute/modify this program under the terms of the GNU
80
98
  General Public License version 3 or later.
81
-
82
-
83
- == Note on Patches/Pull Requests
84
-
85
- * Fork the project.
86
- * Make your feature addition or bug fix.
87
- * Add tests for it. This is important so I don't break it in a
88
- future version unintentionally.
89
- * Commit, do not mess with rakefile, version, or history.
90
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
91
- * Send me a pull request. Bonus points for topic branches.
data/bin/syncache-drb ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # SynCache dRuby object cache server
4
+ # (originally written for Samizdat project)
5
+ #
6
+ # Copyright (c) 2002-2011 Dmitry Borodaenko <angdraug@debian.org>
7
+ #
8
+ # This program is free software.
9
+ # You can distribute/modify this program under the terms of
10
+ # the GNU General Public License version 3 or later.
11
+ #
12
+ # vim: et sw=2 sts=2 ts=8 tw=0
13
+
14
+ require 'getoptlong'
15
+ require 'etc'
16
+ require 'syslog'
17
+ require 'drb'
18
+ require 'syncache'
19
+
20
+ class SynCacheDRb
21
+ PNAME = 'syncache-drb'
22
+
23
+ def usage
24
+ puts %{
25
+ Usage: #{PNAME} [ options ] [ URI ]
26
+
27
+ Options:
28
+
29
+ URI
30
+ URI with druby: schema that DRb server binds to, default is
31
+ druby://localhost:9000
32
+
33
+ --help
34
+ display this message and quit
35
+
36
+ --ttl SECONDS
37
+ time-to-live value for cache entries, default is 24 hours
38
+
39
+ --size ENTRIES
40
+ maximum number of objects in cache, default is 10000
41
+
42
+ --flush-delay SECONDS
43
+ rate-limit flush operations: if less than that number of seconds
44
+ has passed since last flush, next flush will be delayed; default
45
+ is no rate limit
46
+
47
+ --user USER
48
+ Run as USER if started as root. Default is nobody.
49
+
50
+ --error-log ERROR_LOG_PATH
51
+ File to write errors to. Default is /dev/null. When run as root,
52
+ the file is chowned to USER:adm.
53
+
54
+ --debug
55
+ Enable debug mode. If an error log is specified with --error-log,
56
+ all messages will be sent there instead of syslog.
57
+
58
+ --pidfile PATH
59
+ Path to the pidfile. By default, pidfile is created under
60
+ /var/run/#{PNAME}/ when run as root, or under $TMPDIR
61
+ otherwise. Location should be writeable by USER.
62
+
63
+ }
64
+ exit
65
+ end
66
+
67
+ def set_options
68
+ opts = GetoptLong.new(
69
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
70
+ [ '--ttl', '-t', GetoptLong::REQUIRED_ARGUMENT ],
71
+ [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ],
72
+ [ '--flush-delay', '-f', GetoptLong::REQUIRED_ARGUMENT ],
73
+ [ '--user', '-u', GetoptLong::REQUIRED_ARGUMENT ],
74
+ [ '--error-log', '-e', GetoptLong::REQUIRED_ARGUMENT ],
75
+ [ '--debug', '-d', GetoptLong::NO_ARGUMENT ],
76
+ [ '--pidfile', '-p', GetoptLong::REQUIRED_ARGUMENT ]
77
+ )
78
+
79
+ @uri = 'druby://localhost:9000'
80
+ @ttl = 24*60*60
81
+ @size = 10000
82
+ @flush_delay = nil
83
+ @user = 'nobody'
84
+ @error_log = '/dev/null'
85
+ @debug = false
86
+ @pidfile = (0 == Process.uid) ?
87
+ "/var/run/#{PNAME}/#{PNAME}.pid" :
88
+ (ENV.has_key?('TMPDIR') ? ENV['TMPDIR'] : '/tmp') + "/#{PNAME}.pid"
89
+
90
+ opts.each do |opt, arg|
91
+ case opt
92
+ when '--help'
93
+ usage
94
+ when '--ttl'
95
+ @ttl = arg.to_i
96
+ when '--size'
97
+ @size = arg.to_i
98
+ when '--flush-delay'
99
+ @flush_delay = arg.to_i
100
+ when '--user'
101
+ @user = arg.dup.untaint
102
+ when '--error-log'
103
+ @error_log = arg.dup.untaint
104
+ when '--debug'
105
+ @debug = true
106
+ when '--pidfile'
107
+ @pidfile = arg.dup.untaint
108
+ end
109
+ end
110
+
111
+ @uri = ARGV[0].dup.untaint if ARGV[0]
112
+ @user = Etc.getpwnam(@user)
113
+ end
114
+
115
+ def log(message, level = :info)
116
+ if @log.respond_to? level
117
+ @log.send(level, message)
118
+ else
119
+ STDERR << 'syncache: ' + message + "\n"
120
+ STDERR.flush
121
+ end
122
+ end
123
+
124
+ def daemonize
125
+ $0 = PNAME
126
+ ENV.clear
127
+ exit if fork
128
+ Process.setsid
129
+ exit if fork
130
+ Dir.chdir '/'
131
+ File.umask 0022
132
+
133
+ STDIN.reopen '/dev/null'
134
+ STDOUT.reopen '/dev/null', 'a'
135
+ STDERR.reopen @error_log, 'a'
136
+
137
+ if 0 == Process.uid
138
+ if @error_log != '/dev/null'
139
+ File.chown(@user.uid, Etc.getgrnam('adm').gid, @error_log)
140
+ File.chmod(0640, @error_log)
141
+ end
142
+
143
+ # drop root priviledge
144
+ Process::Sys.setgid(@user.gid)
145
+ Process::Sys.setuid(@user.uid)
146
+ end
147
+
148
+ File.open(@pidfile, 'w') {|f| f.puts Process.pid }
149
+ end
150
+
151
+ def open_syslog
152
+ @log = Syslog.open PNAME
153
+ end
154
+
155
+ def ready
156
+ @cache = SynCache::Cache.new(@ttl, @size, @flush_delay) # initialize cache
157
+ @cache.debug = true if @debug
158
+ end
159
+
160
+ def trap_signals
161
+ shutdown = lambda do |sig|
162
+ Thread.new do
163
+ sleep 0.1
164
+ DRb.stop_service
165
+ end
166
+ end
167
+ Signal.trap('INT', shutdown)
168
+ Signal.trap('TERM', shutdown)
169
+ Signal.trap('HUP') do |sig| # kill -HUP to flush cache
170
+ @cache.flush
171
+ log 'cache flushed'
172
+ end
173
+ end
174
+
175
+ def start_service
176
+ $SAFE = 1
177
+ DRb.start_service(@uri, @cache)
178
+ log 'started'
179
+ end
180
+
181
+ def wait_for_shutdown
182
+ DRb.thread.join
183
+ File.delete @pidfile
184
+ log 'shut down'
185
+ end
186
+
187
+ def run
188
+ set_options
189
+ daemonize
190
+ open_syslog unless @debug and @error_log
191
+ begin
192
+ ready
193
+ trap_signals
194
+ start_service
195
+ wait_for_shutdown
196
+ rescue => error
197
+ log "#{error.class.to_s}: #{error.to_s}", :err
198
+ error.backtrace.each {|line| log(' ' + line, :err) }
199
+ raise
200
+ end
201
+ end
202
+ end
203
+
204
+ SynCacheDRb.new.run
@@ -0,0 +1,89 @@
1
+ # SynCache: thread-safe time-limited cache with flexible replacement policy
2
+ # (originally written for Samizdat project)
3
+ #
4
+ # Copyright (c) 2002-2011 Dmitry Borodaenko <angdraug@debian.org>
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU General Public License version 3 or later.
9
+ #
10
+ # vim: et sw=2 sts=2 ts=8 tw=0
11
+
12
+ require 'drb'
13
+ require 'delegate'
14
+
15
+ module SynCache
16
+
17
+ REMOTE_TIMEOUT = 60 * 5 # 5 minutes
18
+ REMOTE_FIRST_DELAY = 0.1 # 100 ms
19
+
20
+ class Placeholder
21
+ def initialize
22
+ @id = rand(9223372036854775808)
23
+ @timestamp = Time.now
24
+ end
25
+
26
+ attr_reader :id, :timestamp
27
+
28
+ def ===(other)
29
+ other.kind_of?(Placeholder) and other.id == @id
30
+ end
31
+ end
32
+
33
+ # Connects to a remote SynCache instance over DRb at the provided URI and
34
+ # replaces the remote fetch_or_add method with a slightly less bullet-proof
35
+ # version that invokes the supplied block locally (instead of sending it over
36
+ # DRb to the cache and then back to a different local thread via a local DRb
37
+ # service).
38
+ #
39
+ # If another RemoteCache client is already working on the same key, this client
40
+ # will wait, using randomly increasing intervals. When a configured timeout
41
+ # runs out (counting from the time the other client has put a placeholder in
42
+ # the cache), the client will give up, discard the other client's placeholder
43
+ # and start working on the key itself.
44
+ #
45
+ # Mixing access to the same cache entries from direct and RemoteCache clients
46
+ # is not recommended.
47
+ #
48
+ class RemoteCache
49
+ def initialize(uri, timeout=REMOTE_TIMEOUT, first_delay=REMOTE_FIRST_DELAY)
50
+ @timeout = timeout
51
+ @first_delay = first_delay
52
+ @cache = DRbObject.new_with_uri(uri)
53
+ end
54
+
55
+ def method_missing(method, *args)
56
+ @cache.send(method, *args)
57
+ end
58
+
59
+ def fetch_or_add(key)
60
+ placeholder = Placeholder.new
61
+ value = @cache.fetch_or_add(key) { placeholder }
62
+
63
+ case value
64
+ when placeholder
65
+ # our placeholder
66
+ value = @cache[key] = yield
67
+
68
+ when Placeholder
69
+ # someone else's placeholder
70
+ delay = @first_delay
71
+ while value.kind_of?(Placeholder) and Time.now < value.timestamp + @timeout
72
+ sleep(delay)
73
+ delay *= 1 + rand
74
+ value = @cache[key]
75
+ end
76
+
77
+ if value.kind_of?(Placeholder)
78
+ # ran out of time: give up and do it ourselves
79
+ @cache.delete(key)
80
+ @cache[key] = Placeholder.new
81
+ value = @cache[key] = yield
82
+ end
83
+ end
84
+
85
+ value
86
+ end
87
+ end
88
+
89
+ end # module SynCache