Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
张重言
rails
提交
e3a746b6
R
rails
项目概览
张重言
/
rails
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
rails
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
e3a746b6
编写于
9月 30, 2012
作者:
B
Brian Durand
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead.
上级
da27fa18
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
141 addition
and
101 deletion
+141
-101
activesupport/CHANGELOG.md
activesupport/CHANGELOG.md
+2
-0
activesupport/lib/active_support/cache.rb
activesupport/lib/active_support/cache.rb
+85
-63
activesupport/lib/active_support/cache/memory_store.rb
activesupport/lib/active_support/cache/memory_store.rb
+1
-0
activesupport/test/caching_test.rb
activesupport/test/caching_test.rb
+53
-38
未找到文件。
activesupport/CHANGELOG.md
浏览文件 @
e3a746b6
...
...
@@ -237,4 +237,6 @@
*
Remove deprecated ActiveSupport::JSON::Variable.
*Erich Menge*
*
Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead.
*Brian Durand*
Please check
[
3-2-stable
](
https://github.com/rails/rails/blob/3-2-stable/activesupport/CHANGELOG.md
)
for previous changes.
activesupport/lib/active_support/cache.rb
浏览文件 @
e3a746b6
...
...
@@ -284,7 +284,9 @@ def fetch(name, options = nil)
end
if
entry
&&
entry
.
expired?
race_ttl
=
options
[
:race_condition_ttl
].
to_i
if
race_ttl
and
Time
.
now
.
to_f
-
entry
.
expires_at
<=
race_ttl
if
race_ttl
&&
(
Time
.
now
-
entry
.
expires_at
<=
race_ttl
)
# When an entry has :race_condition_ttl defined, put the stale entry back into the cache
# for a brief period while the entry is begin recalculated.
entry
.
expires_at
=
Time
.
now
+
race_ttl
write_entry
(
key
,
entry
,
:expires_in
=>
race_ttl
*
2
)
else
...
...
@@ -532,102 +534,122 @@ def log(operation, key, options = nil)
end
end
# Entry that is put into caches. It supports expiration time on entries and
# can compress values to save space in the cache.
class
Entry
attr_reader
:created_at
,
:expires_in
# This class is used to represent cache entries. Cache entries have a value and an optional
# expiration time. The expiration time is used to support the :race_condition_ttl option
# on the cache.
#
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
# using short instance variable names that are lazily defined.
class
Entry
# :nodoc:
DEFAULT_COMPRESS_LIMIT
=
16
.
kilobytes
class
<<
self
# Create an entry with internal attributes set. This method is intended
# to be used by implementations that store cache entries in a native
# format instead of as serialized Ruby objects.
def
create
(
raw_value
,
created_at
,
options
=
{})
entry
=
new
(
nil
)
entry
.
instance_variable_set
(
:@value
,
raw_value
)
entry
.
instance_variable_set
(
:@created_at
,
created_at
.
to_f
)
entry
.
instance_variable_set
(
:@compressed
,
options
[
:compressed
])
entry
.
instance_variable_set
(
:@expires_in
,
options
[
:expires_in
])
entry
end
end
# Create a new cache entry for the specified value. Options supported are
# +:compress+, +:compress_threshold+, and +:expires_in+.
def
initialize
(
value
,
options
=
{})
@compressed
=
false
@expires_in
=
options
[
:expires_in
]
@expires_in
=
@expires_in
.
to_f
if
@expires_in
@created_at
=
Time
.
now
.
to_f
if
value
.
nil?
@value
=
nil
if
should_compress?
(
value
,
options
)
@v
=
compress
(
value
)
@c
=
true
else
@value
=
Marshal
.
dump
(
value
)
if
should_compress?
(
@value
,
options
)
@value
=
Zlib
::
Deflate
.
deflate
(
@value
)
@compressed
=
true
end
@v
=
value
end
end
# Get the raw value. This value may be serialized and compressed.
def
raw_value
@value
end
# Get the value stored in the cache.
def
value
# If the original value was exactly false @value is still true because
# it is marshalled and eventually compressed. Both operations yield
# strings.
if
@value
Marshal
.
load
(
compressed?
?
Zlib
::
Inflate
.
inflate
(
@value
)
:
@value
)
if
expires_in
=
options
[
:expires_in
]
@x
=
(
Time
.
now
+
expires_in
).
to_i
end
end
def
compressed?
@compressed
def
value
convert_version_3_entry!
if
defined?
(
@value
)
compressed?
?
uncompress
(
@v
)
:
@v
end
# Check if the entry is expired. The +expires_in+ parameter can override
# the value set when the entry was created.
def
expired?
@expires_in
&&
@created_at
+
@expires_in
<=
Time
.
now
.
to_f
end
# Set a new time when the entry will expire.
def
expires_at
=
(
time
)
if
time
@expires_in
=
time
.
to_f
-
@created_at
convert_version_3_entry!
if
defined?
(
@value
)
if
defined?
(
@x
)
@x
&&
@x
<
Time
.
now
.
to_i
else
@expires_in
=
nil
false
end
end
# Seconds since the epoch when the entry will expire.
def
expires_at
@expires_in
?
@created_at
+
@expires_in
:
nil
Time
.
at
(
@x
)
if
defined?
(
@x
)
end
def
expires_at
=
(
value
)
@x
=
value
.
to_i
end
# Returns the size of the cached value. This could be less than
# <tt>value.size</tt> if the data is compressed.
def
size
if
@value
.
nil?
0
if
defined?
(
@s
)
@s
else
@value
.
bytesize
case
value
when
NilClass
0
when
String
value
.
bytesize
else
@s
=
Marshal
.
dump
(
value
).
bytesize
end
end
end
# Duplicate the value in a class. This is used by cache implementations that don't natively
# serialize entries to protect against accidental cache modifications.
def
dup_value!
convert_version_3_entry!
if
defined?
(
@value
)
if
@v
&&
!
compressed?
&&
!
(
@v
.
is_a?
(
Numeric
)
||
@v
==
true
||
@v
==
false
)
if
@v
.
is_a?
(
String
)
@v
=
@v
.
dup
else
@v
=
Marshal
.
load
(
Marshal
.
dump
(
@v
))
end
end
end
private
def
should_compress?
(
serialized_
value
,
options
)
if
options
[
:compress
]
def
should_compress?
(
value
,
options
)
if
value
&&
options
[
:compress
]
compress_threshold
=
options
[
:compress_threshold
]
||
DEFAULT_COMPRESS_LIMIT
return
true
if
serialized_value
.
size
>=
compress_threshold
serialized_value_size
=
(
value
.
is_a?
(
String
)
?
value
:
Marshal
.
dump
(
value
)).
bytesize
return
true
if
serialized_value_size
>=
compress_threshold
end
false
end
def
compressed?
defined?
(
@c
)
?
@c
:
false
end
def
compress
(
value
)
Zlib
::
Deflate
.
deflate
(
Marshal
.
dump
(
value
))
end
def
uncompress
(
value
)
Marshal
.
load
(
Zlib
::
Inflate
.
inflate
(
value
))
end
# The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
# to ensure that cache entries created under the old version still work with the new class definition.
def
convert_version_3_entry!
if
defined?
(
@value
)
@v
=
@value
remove_instance_variable
(
:@value
)
end
if
defined?
(
@compressed
)
@c
=
@compressed
remove_instance_variable
(
:@compressed
)
end
if
defined?
(
@expires_in
)
&&
defined?
(
@created_at
)
@x
=
(
@created_at
+
@expires_in
).
to_i
remove_instance_variable
(
:@created_at
)
remove_instance_variable
(
:@expires_in
)
end
end
end
end
end
activesupport/lib/active_support/cache/memory_store.rb
浏览文件 @
e3a746b6
...
...
@@ -135,6 +135,7 @@ def read_entry(key, options) # :nodoc:
end
def
write_entry
(
key
,
entry
,
options
)
# :nodoc:
entry
.
dup_value!
synchronize
do
old_entry
=
@data
[
key
]
return
false
if
@data
.
key?
(
key
)
&&
options
[
:unless_exist
]
...
...
activesupport/test/caching_test.rb
浏览文件 @
e3a746b6
...
...
@@ -224,25 +224,22 @@ def test_read_multi
end
def
test_read_multi_with_expires
@cache
.
write
(
'foo'
,
'bar'
,
:expires_in
=>
0.001
)
time
=
Time
.
now
@cache
.
write
(
'foo'
,
'bar'
,
:expires_in
=>
10
)
@cache
.
write
(
'fu'
,
'baz'
)
@cache
.
write
(
'fud'
,
'biz'
)
sleep
(
0.002
)
Time
.
stubs
(
:now
).
returns
(
time
+
11
)
assert_equal
({
"fu"
=>
"baz"
},
@cache
.
read_multi
(
'foo'
,
'fu'
))
end
def
test_read_and_write_compressed_small_data
@cache
.
write
(
'foo'
,
'bar'
,
:compress
=>
true
)
raw_value
=
@cache
.
send
(
:read_entry
,
'foo'
,
{}).
raw_value
assert_equal
'bar'
,
@cache
.
read
(
'foo'
)
assert_equal
'bar'
,
Marshal
.
load
(
raw_value
)
end
def
test_read_and_write_compressed_large_data
@cache
.
write
(
'foo'
,
'bar'
,
:compress
=>
true
,
:compress_threshold
=>
2
)
raw_value
=
@cache
.
send
(
:read_entry
,
'foo'
,
{}).
raw_value
assert_equal
'bar'
,
@cache
.
read
(
'foo'
)
assert_equal
'bar'
,
Marshal
.
load
(
Zlib
::
Inflate
.
inflate
(
raw_value
))
end
def
test_read_and_write_compressed_nil
...
...
@@ -301,14 +298,6 @@ def test_delete
assert
!
@cache
.
exist?
(
'foo'
)
end
def
test_read_should_return_a_different_object_id_each_time_it_is_called
@cache
.
write
(
'foo'
,
'bar'
)
assert_not_equal
@cache
.
read
(
'foo'
).
object_id
,
@cache
.
read
(
'foo'
).
object_id
value
=
@cache
.
read
(
'foo'
)
value
<<
'bingo'
assert_not_equal
value
,
@cache
.
read
(
'foo'
)
end
def
test_original_store_objects_should_not_be_immutable
bar
=
'bar'
@cache
.
write
(
'foo'
,
bar
)
...
...
@@ -363,7 +352,7 @@ def test_race_condition_protection_is_safe
rescue
ArgumentError
end
assert_equal
"bar"
,
@cache
.
read
(
'foo'
)
Time
.
stubs
(
:now
).
returns
(
time
+
7
1
)
Time
.
stubs
(
:now
).
returns
(
time
+
9
1
)
assert_nil
@cache
.
read
(
'foo'
)
end
...
...
@@ -646,9 +635,9 @@ def test_prune_size
@cache
.
prune
(
@record_size
*
3
)
assert
@cache
.
exist?
(
5
)
assert
@cache
.
exist?
(
4
)
assert
!
@cache
.
exist?
(
3
)
assert
"no entry"
,
!
@cache
.
exist?
(
3
)
assert
@cache
.
exist?
(
2
)
assert
!
@cache
.
exist?
(
1
)
assert
"no entry"
,
!
@cache
.
exist?
(
1
)
end
def
test_prune_size_on_write
...
...
@@ -670,12 +659,12 @@ def test_prune_size_on_write
assert
@cache
.
exist?
(
9
)
assert
@cache
.
exist?
(
8
)
assert
@cache
.
exist?
(
7
)
assert
!
@cache
.
exist?
(
6
)
assert
!
@cache
.
exist?
(
5
)
assert
"no entry"
,
!
@cache
.
exist?
(
6
)
assert
"no entry"
,
!
@cache
.
exist?
(
5
)
assert
@cache
.
exist?
(
4
)
assert
!
@cache
.
exist?
(
3
)
assert
"no entry"
,
!
@cache
.
exist?
(
3
)
assert
@cache
.
exist?
(
2
)
assert
!
@cache
.
exist?
(
1
)
assert
"no entry"
,
!
@cache
.
exist?
(
1
)
end
def
test_pruning_is_capped_at_a_max_time
...
...
@@ -764,6 +753,14 @@ def test_local_cache_raw_values_with_marshal
assert_equal
[],
cache
.
read
(
"foo"
)
end
end
def
test_read_should_return_a_different_object_id_each_time_it_is_called
@cache
.
write
(
'foo'
,
'bar'
)
assert_not_equal
@cache
.
read
(
'foo'
).
object_id
,
@cache
.
read
(
'foo'
).
object_id
value
=
@cache
.
read
(
'foo'
)
value
<<
'bingo'
assert_not_equal
value
,
@cache
.
read
(
'foo'
)
end
end
class
NullStoreTest
<
ActiveSupport
::
TestCase
...
...
@@ -844,15 +841,6 @@ def test_mute_logging
end
class
CacheEntryTest
<
ActiveSupport
::
TestCase
def
test_create_raw_entry
time
=
Time
.
now
entry
=
ActiveSupport
::
Cache
::
Entry
.
create
(
"raw"
,
time
,
:compress
=>
false
,
:expires_in
=>
300
)
assert_equal
"raw"
,
entry
.
raw_value
assert_equal
time
.
to_f
,
entry
.
created_at
assert
!
entry
.
compressed?
assert_equal
300
,
entry
.
expires_in
end
def
test_expired
entry
=
ActiveSupport
::
Cache
::
Entry
.
new
(
"value"
)
assert
!
entry
.
expired?
,
'entry not expired'
...
...
@@ -864,16 +852,43 @@ def test_expired
end
def
test_compress_values
entry
=
ActiveSupport
::
Cache
::
Entry
.
new
(
"value"
,
:compress
=>
true
,
:compress_threshold
=>
1
)
assert_equal
"value"
,
entry
.
value
assert
entry
.
compressed?
assert
_equal
"value"
,
Marshal
.
load
(
Zlib
::
Inflate
.
inflate
(
entry
.
raw_value
)
)
value
=
"value"
*
100
entry
=
ActiveSupport
::
Cache
::
Entry
.
new
(
value
,
:compress
=>
true
,
:compress_threshold
=>
1
)
assert
_equal
value
,
entry
.
value
assert
"value is compressed"
,
(
value
.
bytesize
>
entry
.
size
)
end
def
test_non_compress_values
entry
=
ActiveSupport
::
Cache
::
Entry
.
new
(
"value"
)
assert_equal
"value"
,
entry
.
value
assert_equal
"value"
,
Marshal
.
load
(
entry
.
raw_value
)
assert
!
entry
.
compressed?
value
=
"value"
*
100
entry
=
ActiveSupport
::
Cache
::
Entry
.
new
(
value
)
assert_equal
value
,
entry
.
value
assert_equal
value
.
bytesize
,
entry
.
size
end
def
test_restoring_version_3_entries
version_3_entry
=
ActiveSupport
::
Cache
::
Entry
.
allocate
version_3_entry
.
instance_variable_set
(
:@value
,
"hello"
)
version_3_entry
.
instance_variable_set
(
:@created_at
,
Time
.
now
-
60
)
entry
=
Marshal
.
load
(
Marshal
.
dump
(
version_3_entry
))
assert_equal
"hello"
,
entry
.
value
assert_equal
false
,
entry
.
expired?
end
def
test_restoring_compressed_version_3_entries
version_3_entry
=
ActiveSupport
::
Cache
::
Entry
.
allocate
version_3_entry
.
instance_variable_set
(
:@value
,
Zlib
::
Deflate
.
deflate
(
Marshal
.
dump
(
"hello"
)))
version_3_entry
.
instance_variable_set
(
:@compressed
,
true
)
entry
=
Marshal
.
load
(
Marshal
.
dump
(
version_3_entry
))
assert_equal
"hello"
,
entry
.
value
end
def
test_restoring_expired_version_3_entries
version_3_entry
=
ActiveSupport
::
Cache
::
Entry
.
allocate
version_3_entry
.
instance_variable_set
(
:@value
,
"hello"
)
version_3_entry
.
instance_variable_set
(
:@created_at
,
Time
.
now
-
60
)
version_3_entry
.
instance_variable_set
(
:@expires_in
,
58.9
)
entry
=
Marshal
.
load
(
Marshal
.
dump
(
version_3_entry
))
assert_equal
"hello"
,
entry
.
value
assert_equal
true
,
entry
.
expired?
end
end
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录