Skip to content

Commit 59a3b16

Browse files
author
The one with the braid (she/her) | Dфҿ mit dem Zopf (sie/ihr)
authored
Merge pull request #978 from hivedb/braid/web-performance
feat: improve web performance of BoxCollections
2 parents 73834c9 + 6fba893 commit 59a3b16

File tree

7 files changed

+167
-24
lines changed

7 files changed

+167
-24
lines changed

hive/lib/hive.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import 'package:hive/src/object/hive_object.dart';
1818
import 'package:hive/src/util/extensions.dart';
1919
import 'package:meta/meta.dart';
2020

21-
export 'src/box_collection.dart';
21+
export 'src/box_collection/box_collection_stub.dart'
22+
if (dart.library.html) 'package:hive/src/box_collection/box_collection_indexed_db.dart'
23+
if (dart.library.io) 'package:hive/src/box_collection/box_collection.dart';
2224
export 'src/object/hive_object.dart' show HiveObject, HiveObjectMixin;
2325

2426
part 'src/annotations/hive_field.dart';

hive/lib/src/backend/js/native/backend_manager.dart

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,40 @@ class BackendManager implements BackendManagerInterface {
1919
final databaseName = collection ?? name;
2020
final objectStoreName = collection == null ? 'box' : name;
2121

22-
final db =
22+
var db =
2323
await indexedDB!.open(databaseName, version: 1, onUpgradeNeeded: (e) {
2424
var db = e.target.result as Database;
2525
if (!(db.objectStoreNames ?? []).contains(objectStoreName)) {
2626
db.createObjectStore(objectStoreName);
2727
}
2828
});
2929

30+
// in case the objectStore is not contained, re-open the db and
31+
// update version
32+
if (!(db.objectStoreNames ?? []).contains(objectStoreName)) {
33+
print(
34+
'Creating objectStore $objectStoreName in database $databaseName...');
35+
db = await indexedDB!.open(
36+
databaseName,
37+
version: (db.version ?? 1) + 1,
38+
onUpgradeNeeded: (e) {
39+
var db = e.target.result as Database;
40+
if (!(db.objectStoreNames ?? []).contains(objectStoreName)) {
41+
db.createObjectStore(objectStoreName);
42+
}
43+
},
44+
);
45+
}
46+
47+
print('Got object store $objectStoreName in database $databaseName.');
48+
3049
return StorageBackendJs(db, cipher, objectStoreName);
3150
}
3251

3352
@override
3453
Future<void> deleteBox(String name, String? path, String? collection) async {
54+
print('Delete $name // $collection from disk');
55+
3556
// compatibility for old store format
3657
final databaseName = collection ?? name;
3758
final objectStoreName = collection == null ? 'box' : name;

hive/lib/src/backend/js/native/storage_backend_js.dart

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,26 @@ class StorageBackendJs extends StorageBackend {
201201

202202
@override
203203
Future<void> deleteFromDisk() async {
204-
if (_db.objectStoreNames?.length != 1) {
205-
_db.deleteObjectStore(objectStoreName);
206-
} else {
207-
final indexDB = js.context.hasProperty('window')
208-
? window.indexedDB
209-
: WorkerGlobalScope.instance.indexedDB;
204+
final indexDB = js.context.hasProperty('window')
205+
? window.indexedDB
206+
: WorkerGlobalScope.instance.indexedDB;
207+
208+
print('Delete ${_db.name} // $objectStoreName from disk');
209+
210+
// directly deleting the entire DB if a non-collection Box
211+
if (_db.objectStoreNames?.length == 1) {
210212
await indexDB!.deleteDatabase(_db.name!);
213+
} else {
214+
final db =
215+
await indexDB!.open(_db.name!, version: 1, onUpgradeNeeded: (e) {
216+
var db = e.target.result as Database;
217+
if ((db.objectStoreNames ?? []).contains(objectStoreName)) {
218+
db.deleteObjectStore(objectStoreName);
219+
}
220+
});
221+
if ((db.objectStoreNames ?? []).isEmpty) {
222+
await indexDB.deleteDatabase(_db.name!);
223+
}
211224
}
212225
}
213226

hive/lib/src/box_collection.dart renamed to hive/lib/src/box_collection/box_collection.dart

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import 'dart:convert';
44
import 'package:crypto/crypto.dart';
55
import 'package:hive/hive.dart';
66

7-
class BoxCollection {
7+
import 'box_collection_stub.dart' as implementation;
8+
9+
class BoxCollection implements implementation.BoxCollection {
10+
@override
811
final String name;
12+
@override
913
final Set<String> boxNames;
1014
HiveCipher? _cipher;
1115

@@ -34,9 +38,11 @@ class BoxCollection {
3438
return collection;
3539
}
3640

41+
@override
3742
Future<CollectionBox<V>> openBox<V>(String name,
3843
{bool preload = false,
39-
CollectionBox<V> Function(String, BoxCollection)? boxCreator}) async {
44+
implementation.CollectionBox<V> Function(String, BoxCollection)?
45+
boxCreator}) async {
4046
if (!boxNames.contains(name)) {
4147
throw Exception(
4248
'Box with name $name is not in the known box names of this collection.');
@@ -46,17 +52,22 @@ class BoxCollection {
4652
return _openBoxes[i] as CollectionBox<V>;
4753
}
4854
final boxIdentifier = '${this.name}_$name';
49-
final box = boxCreator?.call(boxIdentifier, this) ??
55+
final box = boxCreator?.call(boxIdentifier, this) as CollectionBox<V>? ??
5056
CollectionBox<V>(boxIdentifier, this);
5157
if (preload) {
52-
box._cachedBox = await Hive.openBox(box.name, encryptionCipher: _cipher);
58+
box._cachedBox = await Hive.openBox(
59+
box.name,
60+
encryptionCipher: _cipher,
61+
collection: name,
62+
);
5363
}
5464
_openBoxes.add(box);
5565
return box;
5666
}
5767

5868
final List<CollectionBox> _openBoxes = [];
5969

70+
@override
6071
Future<void> transaction(
6172
Future<void> Function() action, {
6273
List<String>? boxNames,
@@ -80,20 +91,24 @@ class BoxCollection {
8091
});
8192
}
8293

94+
@override
8395
void close() {
8496
for (final box in _openBoxes) {
8597
box._cachedBox?.close();
8698
}
8799
}
88100

101+
@override
89102
Future<void> deleteFromDisk() => Future.wait(
90103
boxNames.map(Hive.deleteBoxFromDisk),
91104
);
92105
}
93106

94107
/// represents a [Box] being part of a [BoxCollection]
95-
class CollectionBox<V> {
108+
class CollectionBox<V> implements implementation.CollectionBox<V> {
109+
@override
96110
final String name;
111+
@override
97112
final BoxCollection boxCollection;
98113

99114
static final transactionBoxes = <Zone, Set<String>>{};
@@ -104,6 +119,7 @@ class CollectionBox<V> {
104119
return _cachedBox ??= await Hive.openLazyBox<V>(
105120
name,
106121
encryptionCipher: boxCollection._cipher,
122+
collection: boxCollection.name,
107123
);
108124
}
109125

@@ -120,6 +136,7 @@ class CollectionBox<V> {
120136
}
121137
}
122138

139+
@override
123140
Future<List<String>> getAllKeys() async {
124141
final box = await _getBox();
125142
return box.keys
@@ -134,6 +151,7 @@ class CollectionBox<V> {
134151
.toList();
135152
}
136153

154+
@override
137155
Future<Map<String, V>> getAllValues() async {
138156
final box = await _getBox();
139157
final keys = box.keys.toList();
@@ -149,13 +167,15 @@ class CollectionBox<V> {
149167
.map((k, v) => MapEntry(Uri.decodeComponent(k.toString()), v as V));
150168
}
151169

170+
@override
152171
Future<V?> get(String key) async {
153172
key = _toHiveKey(key);
154173
final box = await _getBox();
155174
if (box is LazyBox) return await box.get(key) as V?;
156175
return (box as Box).get(key) as V?;
157176
}
158177

178+
@override
159179
Future<List<V?>> getAll(
160180
List<String> keys,
161181
) async {
@@ -172,7 +192,8 @@ class CollectionBox<V> {
172192
return values;
173193
}
174194

175-
Future<void> put(String key, V val) async {
195+
@override
196+
Future<void> put(String key, V val, [Object? transaction]) async {
176197
if (val == null) {
177198
return delete(key);
178199
}
@@ -181,25 +202,29 @@ class CollectionBox<V> {
181202
await _flushOrMark();
182203
}
183204

205+
@override
184206
Future<void> delete(String key) async {
185207
final box = await _getBox();
186208
await box.delete(_toHiveKey(key));
187209
await _flushOrMark();
188210
}
189211

212+
@override
190213
Future<void> deleteAll(List<String> keys) async {
191214
final hiveKeys = keys.map(_toHiveKey);
192215
final box = await _getBox();
193216
await box.deleteAll(hiveKeys);
194217
await _flushOrMark();
195218
}
196219

220+
@override
197221
Future<void> clear() async {
198222
final box = await _getBox();
199223
await box.deleteAll(box.keys);
200224
await _flushOrMark();
201225
}
202226

227+
@override
203228
Future<void> flush() async {
204229
final box = await _getBox();
205230
// we do *not* await the flushing here. That makes it so that we can execute

0 commit comments

Comments
 (0)