syncache 1.0.0 → 1.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/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