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 getBlobsLength and truncate #368

Merged
merged 5 commits into from
Mar 5, 2024
Merged
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
40 changes: 39 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const ReadyResource = require('ready-resource')
const safetyCatch = require('safety-catch')
const crypto = require('hypercore-crypto')
const Hypercore = require('hypercore')
const { BLOCK_NOT_AVAILABLE } = require('hypercore-errors')
const { BLOCK_NOT_AVAILABLE, BAD_ARGUMENT } = require('hypercore-errors')

const keyEncoding = new SubEncoder('files', 'utf-8')

Expand Down Expand Up @@ -94,6 +94,31 @@ module.exports = class Hyperdrive extends ReadyResource {
return this.corestore.findingPeers()
}

async truncate (version, { blobs = -1 } = {}) {
const blobsVersion = blobs === -1 ? await this.getBlobsLength(version) : blobs
const bl = await this.getBlobs()

if (version > this.core.length || blobsVersion > bl.core.length) {
throw BAD_ARGUMENT('Bad truncation length')
}

await this.core.truncate(version)
await bl.core.truncate(blobsVersion)
}

async getBlobsLength (checkout) {
await this.ready()
if (!checkout) checkout = this.version

const c = this.db.checkout(checkout)

try {
return await getBlobsLength(c)
} finally {
await c.close()
}
}

replicate (isInitiator, opts) {
return this.corestore.replicate(isInitiator, opts)
}
Expand Down Expand Up @@ -645,3 +670,16 @@ function generateContentManifest (m, key) {
prologue: null // TODO: could be configurable through the header still...
}
}

async function getBlobsLength (db) {
let length = 0

for await (const { value } of db.createReadStream()) {
const b = value && value.blob
if (!b) continue
const len = b.blockOffset + b.blockLength
if (len > length) length = len
}

return length
}
71 changes: 71 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1402,6 +1402,77 @@ test('non-compat making of cores', async (t) => {
t.absent(drive.blobs.core.core.compat)
})

test('getBlobsLength happy paths', async t => {
const corestore = new Corestore(RAM.reusable())
const drive = new Hyperdrive(corestore.session())

await drive.put('./file', 'here')
t.is(await drive.getBlobsLength(), 1, 'Correct blobs length 1')

await drive.put('./file', 'here')
t.is(await drive.getBlobsLength(), 2, 'Correct blobs length 2')

t.is(drive.version, 3, 'sanity check')
t.is(await drive.getBlobsLength(2), 1, 'Correct blobs length on explicit checkout')
t.is(await drive.getBlobsLength(3), 2, 'Correct blobs length on explicit checkout to latest')
})

test('getBlobsLength when not ready', async t => {
const corestore = new Corestore(RAM.reusable())
{
const drive = new Hyperdrive(corestore.session())
await drive.put('./file', 'here')
await drive.put('./more', 'here')
await drive.close()
}

{
const drive = new Hyperdrive(corestore)
const length = await drive.getBlobsLength()
t.is(length, 2, 'correct blobs length')
await drive.close()
}
})

test('getBlobsLength of empty drive', async t => {
const corestore = new Corestore(RAM.reusable())
const drive = new Hyperdrive(corestore.session())
const length = await drive.getBlobsLength()
t.is(length, 0, 'empty drive has blobsLength 0')
})

test('truncate happy path', async t => {
const corestore = new Corestore(RAM.reusable())
const drive = new Hyperdrive(corestore.session())
await drive.ready()

t.is(drive.db.core.fork, 0, 'sanity check')
t.is(drive.blobs.core.fork, 0, 'sanity check')

await drive.put('file1', 'here1')
await drive.put('file2', 'here2')
await drive.put('file3', 'here3')

t.is(drive.version, 4, 'sanity check')
t.is(await drive.getBlobsLength(), 3, 'sanity check')

await drive.truncate(3)
t.is(drive.version, 3, 'truncated db correctly')
t.is(await drive.getBlobsLength(), 2, 'truncated blobs correctly')

await drive.put('file3', 'here file 3 post truncation')
t.is(drive.version, 4, 'correct version when putting after truncate')
t.is(await drive.getBlobsLength(), 3, 'correct blobsLength when putting after truncate')
t.is(
b4a.toString(await drive.get('file3')),
'here file 3 post truncation',
'Sanity check'
)

t.is(drive.db.core.fork, 1, 'sanity check on db fork')
t.is(drive.blobs.core.fork, 1, 'sanity check on blobs fork')
})

async function testenv (teardown) {
const corestore = new Corestore(RAM)
await corestore.ready()
Expand Down
Loading