syncache 1.0.0 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +676 -0
- data/ChangeLog.mtn +183 -0
- data/README.rdoc +21 -14
- data/bin/syncache-drb +204 -0
- data/lib/syncache/remote.rb +89 -0
- data/lib/syncache/syncache.rb +283 -0
- data/lib/syncache.rb +3 -248
- data/man/syncache-drb.1 +44 -0
- data/setup.rb +1360 -0
- data/syncache.gemspec +24 -62
- data/test/tc_remote.rb +86 -0
- data/test/{test_syncache.rb → tc_syncache.rb} +5 -8
- data/test/ts_syncache.rb +15 -0
- metadata +49 -57
- data/.document +0 -5
- data/.gitignore +0 -21
- data/LICENSE +0 -20
- data/Rakefile +0 -63
- data/VERSION +0 -1
- data/lib/syncache_sync_patch.rb +0 -36
- data/test/helper.rb +0 -9
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)
|
46
|
-
|
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-
|
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
|