Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(90)

Side by Side Diff: third_party/WebKit/Source/modules/indexeddb/IDBValueWrapping.h

Issue 2822453003: Wrap large IndexedDB values into Blobs before writing to LevelDB. (Closed)
Patch Set: Addressed last round of feedback. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef IDBValueWrapping_h
6 #define IDBValueWrapping_h
7
8 #include "bindings/core/v8/ExceptionState.h"
9 #include "bindings/core/v8/serialization/SerializedScriptValue.h"
10 #include "modules/ModulesExport.h"
11 #include "platform/SharedBuffer.h"
12 #include "platform/wtf/Allocator.h"
13 #include "platform/wtf/RefPtr.h"
14 #include "platform/wtf/Vector.h"
15 #include "public/platform/WebBlobInfo.h"
16 #include "v8/include/v8.h"
17
18 namespace blink {
19
20 class Blob;
21 class BlobDataHandle;
22 class ExceptionState;
23 class IDBValue;
24 class ScriptState;
25 class ScriptValue;
26 class SerializedScriptValue;
27 class SharedBuffer;
28
29 // Logic for serializing V8 values for storage in IndexedDB.
30 //
31 // V8 values are stored on disk using the format implemented in
32 // SerializedScriptValue (SSV), which is essentialy a byte array plus an array
33 // of attached Blobs. For "normal" (not too large) V8 values, the SSV output's
34 // byte array is stored directly in IndexedDB's backing store, together with
35 // references to the attached Blobs.
36 //
37 // "Large" V8 values are wrapped in Blobs, in order to avoid operating the
38 // backing store in a sub-optimal region. Specifically, the byte array in the
39 // SSV output is replaced with a "wrapped value" marker, and stored inside a
40 // Blob that is tacked to the end of the SSV's Blob array. IndexedDB's backing
41 // store receives the "wrapped value" marker and the references to the Blobs,
42 // while the large byte array in the SSV output is handled by the Blob storage
43 // system.
44 //
45 // In summary:
46 // "normal" v8::Value -> SSV -> IDBValue (stores SSV output) -> LevelDB
47 // "large" v8::Value -> SSV -> IDBValue (stores SSV output) ->
48 // Blob (stores SSV output) + IDBValue (stores Blob reference) -> LevelDB
49 //
50 // Full picture that accounts for Blob attachments:
51 // "normal" v8::Value -> SSV (byte array, Blob attachments) ->
52 // IDBValue (bytes: SSV byte array, blobs: SSV Blob attachments) -> LevelDB
53 // "large" v8::Value -> SSV (byte array, Blob attachments) ->
54 // IDBValue (bytes: "wrapped value" marker,
55 // blobs: SSV Blob attachments + [wrapper Blob(SSV byte array)] ->
56 // LevelDB
57 class MODULES_EXPORT IDBValueWrapper {
58 STACK_ALLOCATED();
59
60 public:
61 // Wrapper for an IndexedDB value.
62 //
63 // The serialization process can throw an exception. The caller is responsible
64 // for checking exception_state.
65 IDBValueWrapper(
66 v8::Isolate*,
67 v8::Local<v8::Value>,
68 SerializedScriptValue::SerializeOptions::WasmSerializationPolicy,
69 ExceptionState&);
70
71 // Creates a clone of the serialized value.
72 //
73 // This method is used to fulfill the IndexedDB specification requirement that
74 // a value's key and index keys are extracted from a structured clone of the
75 // value, which avoids the issue of side-effects in custom getters.
76 //
77 // This method cannot be called after WrapIfBiggerThan().
78 void Clone(ScriptState*, ScriptValue* clone);
79
80 // Conditionally wraps the serialized value's byte array into a Blob.
81 //
82 // The byte array is wrapped if its size exceeds max_bytes. In production, the
83 // max_bytes threshold is currently always kWrapThreshold.
84 //
85 // This method must be called before ExtractWireBytes() and cannot be called
86 // after ExtractWireBytes().
87 bool WrapIfBiggerThan(unsigned max_bytes);
88
89 // Obtains the BlobDataHandles from the serialized value's Blob array.
90 //
91 // This method must be called at most once, and must be called after
92 // WrapIfBiggerThan().
93 void ExtractBlobDataHandles(
94 Vector<RefPtr<BlobDataHandle>>* blob_data_handles);
95
96 // Obtains the byte array for the serialized value.
97 //
98 // This method must be called at most once, and must be called after
99 // WrapIfBiggerThan().
100 RefPtr<SharedBuffer> ExtractWireBytes();
101
102 // Obtains WebBlobInfos for the serialized value's Blob array.
103 //
104 // This method must be called at most once, and must be called after
105 // WrapIfBiggerThan().
106 inline Vector<WebBlobInfo>& WrappedBlobInfo() {
107 #if DCHECK_IS_ON()
108 DCHECK(!had_exception_)
109 << "WrapBlobInfo() called on wrapper with serialization exception";
110 #endif // DCHECK_IS_ON()
111 return blob_info_;
112 }
113
114 // Default threshold for WrapIfBiggerThan().
115 //
116 // This should be tuned to achieve a compromise between short-term IndexedDB
117 // throughput and long-term I/O load and memory usage. LevelDB, the underlying
118 // storage for IndexedDB, was not designed with large values in mind. At the
119 // very least, large values will slow down compaction, causing occasional I/O
120 // spikes.
121 static constexpr unsigned kWrapThreshold = 64 * 1024;
122
123 // MIME type used for Blobs that wrap IDBValues.
124 static constexpr const char* kWrapMimeType =
125 "application/vnd.blink-idb-value-wrapper";
126
127 private:
128 // Used to serialize the wrapped value.
129 static void WriteVarint(unsigned value, Vector<char>& output);
130
131 RefPtr<SerializedScriptValue> serialized_value_;
132 RefPtr<BlobDataHandle> wrapper_handle_;
133 Vector<WebBlobInfo> blob_info_;
134 Vector<char> wire_bytes_;
135 #if DCHECK_IS_ON()
136 bool had_exception_ = false;
137 bool wrap_called_ = false;
138 #endif // DCHECK_IS_ON()
139 };
140
141 // State and logic for unwrapping large IndexedDB values from Blobs.
142 //
143 // See IDBValueWrapper for an explanation of the wrapping concept.
144 //
145 // Once created, an IDBValueUnwrapper instance can be used to unwrap multiple
146 // Blobs. For each Blob to be unwrapped, the caller should first call Parse().
147 // If the method succeeds, the IDBValueUnwrapper will store the parse state,
148 // which can be obtained using WrapperBlobSize() and WrapperBlobHandle().
149 class MODULES_EXPORT IDBValueUnwrapper {
150 STACK_ALLOCATED();
151
152 public:
153 IDBValueUnwrapper();
154
155 // True if the IDBValue's data was wrapped in a Blob.
156 static bool IsWrapped(IDBValue*);
157
158 // True if at least one of the IDBValues' data was wrapped in a Blob.
159 static bool IsWrapped(const Vector<RefPtr<IDBValue>>&);
160
161 // Pieces together an unwrapped IDBValue from a wrapped value and Blob data.
162 static RefPtr<IDBValue> Unwrap(IDBValue* wrapped_value,
163 RefPtr<SharedBuffer>&& wrapper_blob_content);
164
165 // Parses the wrapper Blob information from a wrapped IDBValue.
166 //
167 // Returns true for success, and false for failure. Failure can mean that the
168 // given value was not a wrapped IDBValue, or that the value bytes were
169 // corrupted.
170 bool Parse(IDBValue*);
171
172 // Returns the size of the Blob obtained by the last Unwrap() call.
173 //
174 // Should only be called after a successful result from Unwrap().
175 inline unsigned WrapperBlobSize() const {
176 DCHECK(end_);
177 return blob_size_;
178 }
179
180 // Returns a handle to the Blob obtained by the last Unwrap() call.
181 //
182 // Should only be called exactly once after a successful result from Unwrap().
183 RefPtr<BlobDataHandle> WrapperBlobHandle();
184
185 private:
186 // Used to deserialize the wrapped value.
187 bool ReadVarint(unsigned& value);
188
189 // Resets the parsing state.
190 inline bool Reset() {
191 #if DCHECK_IS_ON()
192 blob_handle_.Clear();
193 current_ = nullptr;
194 end_ = nullptr;
195 #endif // DCHECK_IS_ON()
196 return false;
197 }
198
199 // Deserialization cursor in the SharedBuffer of the IDBValue being unwrapped.
200 const uint8_t* current_;
201
202 // Smallest invalid position_ value.
203 const uint8_t* end_;
204
205 // The size of the Blob holding the data for the last unwrapped IDBValue.
206 unsigned blob_size_;
207
208 // Handle to the Blob holding the data for the last unwrapped IDBValue.
209 RefPtr<BlobDataHandle> blob_handle_;
210 };
211
212 } // namespace blink
213
214 #endif // IDBValueWrapping_h
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698