Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for RocksDB (dramatic memory improvement with multiple databases) #117

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/pouchdb-adapter-asyncstorage/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ db.get('4711')

```

### Memory

Be careful when using this with other AsyncStorage data or multiple databases as it loads *all* keys in your store into memory. You can use [react-native-async-storage-rocks](https://github.com/tradle/react-native-async-storage-rocks) if this is an issue for you (see below).


### Android limit

On Android asyncstorage has a limitation of 6 MB per default, you might want to increase it
Expand All @@ -34,6 +39,11 @@ long size = 50L * 1024L * 1024L; // 50 MB
com.facebook.react.modules.storage.ReactDatabaseSupplier.getInstance(getApplicationContext()).setMaximumSize(size);
```

### RocksDB

If you are using [react-native-async-storage-rocks](https://github.com/tradle/react-native-async-storage-rocks) then pouchdb-adapter-asyncstorage will detect and use the native `getKeysWithPrefix` method for memory-efficient key lookups.


For full API documentation and guides on PouchDB, see [PouchDB.com](http://pouchdb.com/). For details on PouchDB sub-packages, see the [Custom Builds documentation](http://pouchdb.com/custom.html).

---
Expand Down
79 changes: 56 additions & 23 deletions packages/pouchdb-adapter-asyncstorage/src/asyncstorage_core.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,43 @@
import { AsyncStorage } from 'react-native'
import { safeJsonParse, safeJsonStringify } from 'pouchdb-json'


let AsyncStorageRocksDB
try {
AsyncStorageRocksDB = require('react-native-async-storage-rocks').default
} finally {
// Fall back to regular AsyncStorage
}


function createPrefix(dbName) {
return dbName.replace(/!/g, '!!') + '!' // escape bangs in dbName
}

function prepareKey(key, core) {
return (
core._prefix +
key
.replace(/\u0002/g, '\u0002\u0002')
.replace(/\u0001/g, '\u0001\u0002')
.replace(/\u0000/g, '\u0001\u0001')
)
key = key
.replace(/\u0002/g, '\u0002\u0002')
.replace(/\u0001/g, '\u0001\u0002')
.replace(/\u0000/g, '\u0001\u0001')

if (AsyncStorageRocksDB) {
key = key.replace(/ÿ/g, '\u0003') // RocksDB can't handle ÿ
}

return core._prefix + key
}

function unescapeKey(key) {
key = key
.replace(/\u0001\u0001/g, '\u0000')
.replace(/\u0001\u0002/g, '\u0001')
.replace(/\u0002\u0002/g, '\u0002')

if (AsyncStorageRocksDB) {
key = key.replace(/\u0003/g, 'ÿ')
}

return key
}

function AsyncStorageCore(dbName) {
Expand All @@ -30,24 +55,32 @@ AsyncStorageCore.prototype.getKeys = function(callback) {
const prefix = this._prefix
const prefixLen = prefix.length

AsyncStorage.getAllKeys((error, allKeys) => {
if (error) return callback(error)
if (AsyncStorageRocksDB) {

// getAllKeysWithPrefix is dramatically more memory efficient if there are other items
// stored that are not part of the current database (eg other databases or app data).
// getAllKeys causes memory to increase linearly with the total number of keys.
AsyncStorageRocksDB.getAllKeysWithPrefix(prefix, (error, allKeys) => {
if (error) return callback(error)

allKeys.forEach(fullKey => {
if (fullKey.slice(0, prefixLen) === prefix) {
keys.push(
fullKey
.slice(prefixLen)
.replace(/\u0001\u0001/g, '\u0000')
.replace(/\u0001\u0002/g, '\u0001')
.replace(/\u0002\u0002/g, '\u0002')
)
}
allKeys.forEach(fullKey => keys.push(unescapeKey(fullKey)))
})

keys.sort()
callback(null, keys)
})
} else {

AsyncStorage.getAllKeys((error, allKeys) => {
if (error) return callback(error)

allKeys.forEach(fullKey => {
if (fullKey.slice(0, prefixLen) === prefix) {
keys.push(unescapeKey(fullKey.slice(prefixLen)))
}
})
})
}

keys.sort()
callback(null, keys)
}

const stringifyValue = value => {
Expand Down Expand Up @@ -114,4 +147,4 @@ AsyncStorageCore.destroy = function(dbName, callback) {
})
}

module.exports = AsyncStorageCore
module.exports = AsyncStorageCore