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

Unified 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, 7 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/modules/indexeddb/IDBValueWrapping.h
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBValueWrapping.h b/third_party/WebKit/Source/modules/indexeddb/IDBValueWrapping.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1e806e24593860611a6906341e6d740963b856d
--- /dev/null
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBValueWrapping.h
@@ -0,0 +1,214 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IDBValueWrapping_h
+#define IDBValueWrapping_h
+
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/serialization/SerializedScriptValue.h"
+#include "modules/ModulesExport.h"
+#include "platform/SharedBuffer.h"
+#include "platform/wtf/Allocator.h"
+#include "platform/wtf/RefPtr.h"
+#include "platform/wtf/Vector.h"
+#include "public/platform/WebBlobInfo.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class Blob;
+class BlobDataHandle;
+class ExceptionState;
+class IDBValue;
+class ScriptState;
+class ScriptValue;
+class SerializedScriptValue;
+class SharedBuffer;
+
+// Logic for serializing V8 values for storage in IndexedDB.
+//
+// V8 values are stored on disk using the format implemented in
+// SerializedScriptValue (SSV), which is essentialy a byte array plus an array
+// of attached Blobs. For "normal" (not too large) V8 values, the SSV output's
+// byte array is stored directly in IndexedDB's backing store, together with
+// references to the attached Blobs.
+//
+// "Large" V8 values are wrapped in Blobs, in order to avoid operating the
+// backing store in a sub-optimal region. Specifically, the byte array in the
+// SSV output is replaced with a "wrapped value" marker, and stored inside a
+// Blob that is tacked to the end of the SSV's Blob array. IndexedDB's backing
+// store receives the "wrapped value" marker and the references to the Blobs,
+// while the large byte array in the SSV output is handled by the Blob storage
+// system.
+//
+// In summary:
+// "normal" v8::Value -> SSV -> IDBValue (stores SSV output) -> LevelDB
+// "large" v8::Value -> SSV -> IDBValue (stores SSV output) ->
+// Blob (stores SSV output) + IDBValue (stores Blob reference) -> LevelDB
+//
+// Full picture that accounts for Blob attachments:
+// "normal" v8::Value -> SSV (byte array, Blob attachments) ->
+// IDBValue (bytes: SSV byte array, blobs: SSV Blob attachments) -> LevelDB
+// "large" v8::Value -> SSV (byte array, Blob attachments) ->
+// IDBValue (bytes: "wrapped value" marker,
+// blobs: SSV Blob attachments + [wrapper Blob(SSV byte array)] ->
+// LevelDB
+class MODULES_EXPORT IDBValueWrapper {
+ STACK_ALLOCATED();
+
+ public:
+ // Wrapper for an IndexedDB value.
+ //
+ // The serialization process can throw an exception. The caller is responsible
+ // for checking exception_state.
+ IDBValueWrapper(
+ v8::Isolate*,
+ v8::Local<v8::Value>,
+ SerializedScriptValue::SerializeOptions::WasmSerializationPolicy,
+ ExceptionState&);
+
+ // Creates a clone of the serialized value.
+ //
+ // This method is used to fulfill the IndexedDB specification requirement that
+ // a value's key and index keys are extracted from a structured clone of the
+ // value, which avoids the issue of side-effects in custom getters.
+ //
+ // This method cannot be called after WrapIfBiggerThan().
+ void Clone(ScriptState*, ScriptValue* clone);
+
+ // Conditionally wraps the serialized value's byte array into a Blob.
+ //
+ // The byte array is wrapped if its size exceeds max_bytes. In production, the
+ // max_bytes threshold is currently always kWrapThreshold.
+ //
+ // This method must be called before ExtractWireBytes() and cannot be called
+ // after ExtractWireBytes().
+ bool WrapIfBiggerThan(unsigned max_bytes);
+
+ // Obtains the BlobDataHandles from the serialized value's Blob array.
+ //
+ // This method must be called at most once, and must be called after
+ // WrapIfBiggerThan().
+ void ExtractBlobDataHandles(
+ Vector<RefPtr<BlobDataHandle>>* blob_data_handles);
+
+ // Obtains the byte array for the serialized value.
+ //
+ // This method must be called at most once, and must be called after
+ // WrapIfBiggerThan().
+ RefPtr<SharedBuffer> ExtractWireBytes();
+
+ // Obtains WebBlobInfos for the serialized value's Blob array.
+ //
+ // This method must be called at most once, and must be called after
+ // WrapIfBiggerThan().
+ inline Vector<WebBlobInfo>& WrappedBlobInfo() {
+#if DCHECK_IS_ON()
+ DCHECK(!had_exception_)
+ << "WrapBlobInfo() called on wrapper with serialization exception";
+#endif // DCHECK_IS_ON()
+ return blob_info_;
+ }
+
+ // Default threshold for WrapIfBiggerThan().
+ //
+ // This should be tuned to achieve a compromise between short-term IndexedDB
+ // throughput and long-term I/O load and memory usage. LevelDB, the underlying
+ // storage for IndexedDB, was not designed with large values in mind. At the
+ // very least, large values will slow down compaction, causing occasional I/O
+ // spikes.
+ static constexpr unsigned kWrapThreshold = 64 * 1024;
+
+ // MIME type used for Blobs that wrap IDBValues.
+ static constexpr const char* kWrapMimeType =
+ "application/vnd.blink-idb-value-wrapper";
+
+ private:
+ // Used to serialize the wrapped value.
+ static void WriteVarint(unsigned value, Vector<char>& output);
+
+ RefPtr<SerializedScriptValue> serialized_value_;
+ RefPtr<BlobDataHandle> wrapper_handle_;
+ Vector<WebBlobInfo> blob_info_;
+ Vector<char> wire_bytes_;
+#if DCHECK_IS_ON()
+ bool had_exception_ = false;
+ bool wrap_called_ = false;
+#endif // DCHECK_IS_ON()
+};
+
+// State and logic for unwrapping large IndexedDB values from Blobs.
+//
+// See IDBValueWrapper for an explanation of the wrapping concept.
+//
+// Once created, an IDBValueUnwrapper instance can be used to unwrap multiple
+// Blobs. For each Blob to be unwrapped, the caller should first call Parse().
+// If the method succeeds, the IDBValueUnwrapper will store the parse state,
+// which can be obtained using WrapperBlobSize() and WrapperBlobHandle().
+class MODULES_EXPORT IDBValueUnwrapper {
+ STACK_ALLOCATED();
+
+ public:
+ IDBValueUnwrapper();
+
+ // True if the IDBValue's data was wrapped in a Blob.
+ static bool IsWrapped(IDBValue*);
+
+ // True if at least one of the IDBValues' data was wrapped in a Blob.
+ static bool IsWrapped(const Vector<RefPtr<IDBValue>>&);
+
+ // Pieces together an unwrapped IDBValue from a wrapped value and Blob data.
+ static RefPtr<IDBValue> Unwrap(IDBValue* wrapped_value,
+ RefPtr<SharedBuffer>&& wrapper_blob_content);
+
+ // Parses the wrapper Blob information from a wrapped IDBValue.
+ //
+ // Returns true for success, and false for failure. Failure can mean that the
+ // given value was not a wrapped IDBValue, or that the value bytes were
+ // corrupted.
+ bool Parse(IDBValue*);
+
+ // Returns the size of the Blob obtained by the last Unwrap() call.
+ //
+ // Should only be called after a successful result from Unwrap().
+ inline unsigned WrapperBlobSize() const {
+ DCHECK(end_);
+ return blob_size_;
+ }
+
+ // Returns a handle to the Blob obtained by the last Unwrap() call.
+ //
+ // Should only be called exactly once after a successful result from Unwrap().
+ RefPtr<BlobDataHandle> WrapperBlobHandle();
+
+ private:
+ // Used to deserialize the wrapped value.
+ bool ReadVarint(unsigned& value);
+
+ // Resets the parsing state.
+ inline bool Reset() {
+#if DCHECK_IS_ON()
+ blob_handle_.Clear();
+ current_ = nullptr;
+ end_ = nullptr;
+#endif // DCHECK_IS_ON()
+ return false;
+ }
+
+ // Deserialization cursor in the SharedBuffer of the IDBValue being unwrapped.
+ const uint8_t* current_;
+
+ // Smallest invalid position_ value.
+ const uint8_t* end_;
+
+ // The size of the Blob holding the data for the last unwrapped IDBValue.
+ unsigned blob_size_;
+
+ // Handle to the Blob holding the data for the last unwrapped IDBValue.
+ RefPtr<BlobDataHandle> blob_handle_;
+};
+
+} // namespace blink
+
+#endif // IDBValueWrapping_h

Powered by Google App Engine
This is Rietveld 408576698