large_object_store 1.5.0 → 1.6.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.
- checksums.yaml +4 -4
- data/Readme.md +13 -0
- data/lib/large_object_store/version.rb +1 -1
- data/lib/large_object_store.rb +38 -10
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e41ad488600c979693e5e2c58958c8fd9eb3f3d51ae53c908415b920f1180a9
|
4
|
+
data.tar.gz: 22063ad5444b0f50a39c48a08e52e537bba6b21f31b8ee7751d331dfb3574412
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e76ee6a7a7679cdf1b797aa65e3d59656dafeade0240db262de7312d640fdc15e3de1af92323aaed10a8e2d26c762265afc1ae9bb05978b39220224096b0276
|
7
|
+
data.tar.gz: '05937831063dbe4d29766cb4237b82b11fd950a3c879d6fe9461cbf557493ed235b0e181447cca254a9043166145c278b3c1171ad7f9c9f81ed92e70b5864b1a'
|
data/Readme.md
CHANGED
@@ -26,6 +26,19 @@ store.write("a" * 1000, compress: true, compress_limit: 100) # compress when gre
|
|
26
26
|
store.write("a" * 1000, raw: true) # store as string to avoid marshaling overhead
|
27
27
|
```
|
28
28
|
|
29
|
+
zstd
|
30
|
+
====
|
31
|
+
|
32
|
+
[zstd compression](https://engineering.fb.com/2016/08/31/core-data/smaller-and-faster-data-compression-with-zstandard/), a modern improvement over the venerable zlib compression algorithm, is supported by passing the `zstd` flag when writing items:
|
33
|
+
|
34
|
+
```
|
35
|
+
store.write("a" * 10_000_000, compress: true, zstd: true)
|
36
|
+
```
|
37
|
+
|
38
|
+
For backwards compatibility and to enable safe roll-out of the change in working systems, the `zstd` flag defaults to `false`.
|
39
|
+
|
40
|
+
zstd decompression is used when the zstd magic number is detected at the beginning of compressed data, so `zstd: true` does not need to be passed when reading/fetching items.
|
41
|
+
|
29
42
|
Author
|
30
43
|
======
|
31
44
|
[Ana Martinez](https://github.com/anamartinez)<br/>
|
data/lib/large_object_store.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require "large_object_store/version"
|
2
2
|
require "zlib"
|
3
|
+
require "zstd-ruby"
|
3
4
|
require "securerandom"
|
4
5
|
|
5
6
|
module LargeObjectStore
|
6
7
|
UUID_BYTES = 16
|
7
8
|
UUID_SIZE = UUID_BYTES * 2
|
8
|
-
CACHE_VERSION =
|
9
|
+
CACHE_VERSION = 4
|
9
10
|
MAX_OBJECT_SIZE = 1024**2
|
10
11
|
ITEM_HEADER_SIZE = 100
|
11
12
|
DEFAULT_COMPRESS_LIMIT = 16*1024
|
@@ -13,6 +14,8 @@ module LargeObjectStore
|
|
13
14
|
COMPRESSED = 1
|
14
15
|
RAW = 2
|
15
16
|
FLAG_RADIX = 32 # we can store 32 different states
|
17
|
+
ZSTD_MAGIC = "\x28\xB5\x2F\xFD".force_encoding('ASCII-8BIT')
|
18
|
+
ZSTD_COMPRESS_LEVEL = 3 # Default level recommended by zstd authors
|
16
19
|
|
17
20
|
def self.wrap(*args)
|
18
21
|
RailsWrapper.new(*args)
|
@@ -31,7 +34,7 @@ module LargeObjectStore
|
|
31
34
|
|
32
35
|
def write(key, value, **options)
|
33
36
|
options = options.dup
|
34
|
-
value = serialize(value,
|
37
|
+
value = serialize(value, options)
|
35
38
|
|
36
39
|
# calculate slice size; note that key length is a factor because
|
37
40
|
# the key is stored on the same slab page as the value
|
@@ -107,7 +110,7 @@ module LargeObjectStore
|
|
107
110
|
|
108
111
|
# convert a object to a string
|
109
112
|
# modifies options
|
110
|
-
def serialize(value,
|
113
|
+
def serialize(value, options)
|
111
114
|
flag = NORMAL
|
112
115
|
|
113
116
|
if options.delete(:raw)
|
@@ -117,28 +120,53 @@ module LargeObjectStore
|
|
117
120
|
value = @serializer.dump(value)
|
118
121
|
end
|
119
122
|
|
120
|
-
if compress?(value,
|
123
|
+
if compress?(value, options)
|
121
124
|
flag |= COMPRESSED
|
122
|
-
value =
|
125
|
+
value = compress(value, options)
|
123
126
|
end
|
124
127
|
|
125
128
|
value.prepend(flag.to_s(FLAG_RADIX))
|
126
129
|
end
|
127
130
|
|
131
|
+
def compress(value, options)
|
132
|
+
if options[:zstd]
|
133
|
+
Zstd.compress(value, ZSTD_COMPRESS_LEVEL)
|
134
|
+
else
|
135
|
+
Zlib::Deflate.deflate(value)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def decompress(data)
|
140
|
+
if data.start_with?(ZSTD_MAGIC)
|
141
|
+
Zstd.decompress(data)
|
142
|
+
else
|
143
|
+
Zlib::Inflate.inflate(data)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
128
147
|
# opposite operations and order of serialize
|
129
148
|
def deserialize(raw_data)
|
130
149
|
data = raw_data.dup
|
131
150
|
flag = data.slice!(0, 1).to_i(FLAG_RADIX)
|
132
|
-
data =
|
151
|
+
data = decompress(data) if flag & COMPRESSED == COMPRESSED
|
133
152
|
data = @serializer.load(data) if flag & RAW != RAW
|
134
153
|
data
|
135
154
|
end
|
136
155
|
|
137
156
|
# Don't pass compression on to Rails, we're doing it ourselves.
|
138
|
-
def compress?(value,
|
139
|
-
return unless options
|
140
|
-
|
141
|
-
|
157
|
+
def compress?(value, options)
|
158
|
+
return unless options[:compress]
|
159
|
+
|
160
|
+
compress_limit = options[:compress_limit] || DEFAULT_COMPRESS_LIMIT
|
161
|
+
should_compress = value.bytesize > compress_limit
|
162
|
+
|
163
|
+
if should_compress
|
164
|
+
# Pass compress: false to Rails in case the default is true
|
165
|
+
options[:compress] = false
|
166
|
+
options.delete(:compress_limit)
|
167
|
+
end
|
168
|
+
|
169
|
+
should_compress
|
142
170
|
end
|
143
171
|
|
144
172
|
def key(key, i)
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: large_object_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ana Martinez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
12
|
-
dependencies:
|
11
|
+
date: 2023-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: zstd-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.5.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.5.5
|
13
27
|
description:
|
14
28
|
email: acemacu@gmail.com
|
15
29
|
executables: []
|
@@ -31,7 +45,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
45
|
requirements:
|
32
46
|
- - ">="
|
33
47
|
- !ruby/object:Gem::Version
|
34
|
-
version: '2.
|
48
|
+
version: '2.6'
|
35
49
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
50
|
requirements:
|
37
51
|
- - ">="
|