Skip to content
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
64 changes: 38 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,23 @@

<details><summary>Click to expand</summary>

- [Install](#install)
- [Usage](#usage)
- [Contributing](#contributing)
- [Donate](#donate)
- [License](#license)

</details>

## Install
With [npm](https://npmjs.org) do:

```
npm install level-ttl
```

Usage from TypeScript also requires `npm install @types/readable-stream`.

## Usage

**If you are upgrading:** please see [`UPGRADING.md`](UPGRADING.md).
Expand All @@ -31,31 +41,29 @@ Augment `levelup` to handle a new `ttl` option on `put()` and `batch()` that spe
Requires [`levelup`][levelup], [`level`][level] or one of its variants like [`level-rocksdb`][level-rocksdb] to be installed separately.

```js
const level = require('level')
const ttl = require('level-ttl')
import level from 'classic-level'
import ttl from 'level-ttl'

const db = ttl(level('./db'))

// This entry will only stay in the store for 1 hour
db.put('foo', 'bar', { ttl: 1000 * 60 * 60 }, (err) => {
// ..
})
await db.put('foo', 'bar', { ttl: 1000 * 60 * 60 })

db.batch([
await db.batch([
// Same for these two entries
{ type: 'put', key: 'foo', value: 'bar' },
{ type: 'put', key: 'bam', value: 'boom' },
{ type: 'del', key: 'w00t' }
], { ttl: 1000 * 60 * 5 }, (err) => {})
], { ttl: 1000 * 60 * 5 })
```

If you put the same entry twice, you **refresh** the TTL to the _last_ put operation. In this way you can build utilities like [session managers](https://github.com/rvagg/node-level-session/) for your web application where the user's session is refreshed with each visit but expires after a set period of time since their last visit.

Alternatively, for a lower write-footprint you can use the `ttl()` method that is added to your `levelup` instance which can serve to insert or update a ttl for any given key in the database - even if that key doesn't exist but may in the future!

```js
db.put('foo', 'bar', (err) => {})
db.ttl('foo', 1000 * 60 * 60, (err) => {})
await db.put('foo', 'bar')
await db.ttl('foo', 1000 * 60 * 60)
```

`level-ttl` uses an internal scan every 10 seconds by default, this limits the available resolution of your TTL values, possibly delaying a delete for up to 10 seconds. The resolution can be tuned by passing the `checkFrequency` option to the `ttl()` initialiser.
Expand All @@ -78,8 +86,8 @@ const db = ttl(level('./db'), {
defaultTTL: 15 * 60 * 1000
})

db.put('A', 'beep', (err) => {})
db.put('B', 'boop', { ttl: 60 * 1000 }, (err) => {})
await db.put('A', 'beep')
await db.put('B', 'boop', { ttl: 60 * 1000 })
```

### `opts.sub`
Expand All @@ -89,28 +97,32 @@ You can provide a custom storage for the meta data by using the `opts.sub` prope
A db for the data and a separate to store the meta data:

```js
const level = require('level')
const ttl = require('level-ttl')
const meta = level('./meta')
import level from 'classic-level'
import ttl from 'level-ttl'
import { EntryStream } from 'level-read-stream'

const rootDb = level('./db')
const meta = rootDb.sublevel('meta')

const db = ttl(level('./db'), { sub: meta })
const db = ttl(rootDb, { sub: meta })

const batch = [
{ type: 'put', key: 'foo', value: 'foo value' },
{ type: 'put', key: 'bar', value: 'bar value' }
]

db.batch(batch, { ttl: 100 }, function (err) {
db.createReadStream()
.on('data', function (data) {
console.log('data', data)
})
.on('end', function () {
meta.createReadStream()
.on('data', function (data) {
console.log('meta', data)
})
})
await db.batch(batch, { ttl: 100 })

new EntryStream(db)
.on('data', function (data) {
console.log('data', data)
})
.on('end', function () {
new EntryStream(meta)
.on('data', function (data) {
console.log('meta', data)
})
})
})
```

Expand Down
8 changes: 2 additions & 6 deletions encoding.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
'use strict'

exports.create = function createEncoding (options) {
options || (options = {})

export function createEncoding (options = {}) {
if (options.ttlEncoding) return options.ttlEncoding

const PATH_SEP = options.separator
Expand All @@ -14,7 +10,7 @@ exports.create = function createEncoding (options) {
}

return {
buffer: false,
format: 'utf8',
encode: function (e) {
// TODO: reexamine this with respect to level-sublevel@6's native codecs
if (Array.isArray(e)) {
Expand Down
40 changes: 40 additions & 0 deletions level-ttl.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { AbstractLevel, AbstractPutOptions, AbstractBatchOptions } from 'abstract-level'
import type { Encoding } from 'level-transcoder'

export interface LevelTtlOptions {
defaultTTL: number
checkFrequency: number
ttlEncoding?: Encoding
sub?: AbstractLevel
namespace: string
methodPrefix: string
expiryNamespace: string
separator: string
}

export interface LevelTtlOpsExtraOptions {
ttl?: number
}

export interface LevelTtlPutOptions <K, V> extends AbstractPutOptions <K, V>, LevelTtlOpsExtraOptions {}

export interface LevelTtlBatchOptions <K, V> extends AbstractBatchOptions <K, V>, LevelTtlOpsExtraOptions {}

export interface _TTL extends Pick<AbstractLevel, 'put' | 'del' | 'batch' | 'close'> {
sub?: AbstractLevel
options: LevelTtlOptions
encoding: Encoding
_prefixNs: string[]
_expiryNs: string[]
_lock: AsyncLock
}

declare function LevelTTL <DB extends AbstractLevel> (db: DB, options: Partial<LevelTtlOptions>): DB & {
put: <K = string, V = string> (key: K, value: V, options: LevelTtlPutOptions) => Promise<void>
batch: <K = string, V = string> (operations: Array<AbstractBatchOperation<typeof this, K, V>>, options: LevelTtlBatchOptions) => Promise<void>
ttl: <K = string> (key: K, delay: number) => Promise<void>
stop: () => void
_ttl: _TTL
}

export default LevelTTL
Loading