LevelBlockstore is closed when re-initializing a Helia instance

I have an interesting issue when working with Helia and Level Blockstore. When I re-initialize an instance of Helia with an existing blockstore the blockstore’s db is closed and the error Error: Database is not open is thrown.

Even if I complete delete the Blockstore’s directory the problem still occurs so I’m figuring something is floating around in memory.

I’m working with Mocha where each test run initializes helia during beforeEach and afterEach tears helia down:

Here is a simplified example of the problem I am experiencing in my beforeEach/afterEach initialization pattern:

import { createHelia } from 'helia'
import { LevelBlockstore } from 'blockstore-level'
import * as Block from 'multiformats/block'
import * as dagCbor from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
import { rimraf } from 'rimraf'

const main = async () => {  
  const createHeliaInstance = async () => {            
      let blockstore = new LevelBlockstore('./helia')
      return createHelia({ blockstore })
  }
  
  const codec = dagCbor
  const hasher = sha256

  const block = await Block.encode({ value: 'hello world', codec, hasher })
  
  // beforeEach
  let helia = await createHeliaInstance()

  // afterEach
  await helia.stop()
  await rimraf('./helia')

  // beforeEach
  helia = await createHeliaInstance()
  
  try {
    // afterEach
    // this fails with "Database is not open"
    await helia.blockstore.put(block.cid, 'hello world')
  } catch (error) {
    console.log('error', error)
  } finally {
    await helia.stop()
    await rimraf('./helia')
  }
}

main()

The second Helia instantiation will result in a DB closed error when I try and put anything to helia.blockstore.

Hopefully this provides a better example using Mocha:

import { createHelia } from 'helia'
import { LevelBlockstore } from 'blockstore-level'
import * as Block from 'multiformats/block'
import * as dagCbor from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
import { rimraf } from 'rimraf'

describe.only('DB Close Issue', function () {
  const createHeliaInstance = async () => {        
    let blockstore = new LevelBlockstore('./helia')
    return createHelia({ blockstore })
  }
  
  let helia
    
  beforeEach(async () => {
    helia = await createHeliaInstance()      
  })
  
  afterEach(async () => {  
    await helia.stop()
    await rimraf('./helia')    
  })

  it('puts a block', async () => {
    const codec = dagCbor
    const hasher = sha256      
    const block = await Block.encode({ value: 'hello world', codec, hasher })
      
    try {
      await helia.blockstore.put(block.cid, 'hello world')
    } catch (error) {
      console.log('error', error)
    }
  })
  
  it('puts a block but throws an error', async () => {
    const codec = dagCbor
    const hasher = sha256      
    const block = await Block.encode({ value: 'hello world', codec, hasher })
      
    try {
      // this fails with "Database is not open"
      await helia.blockstore.put(block.cid, 'hello world')
    } catch (error) {
      console.log('error', error)
    }
  })  
})

The second test fails with a db not open error.

Managed to find a solution:

  afterEach(async () => {
    await heila.blockstore.child.child.close()
    await helia.stop()
    await rimraf('./helia')    
  })

One needs to reach inside blockstore’s composite storage and close the level datastore. This is accomplished by child.child. It seems quite cumbersome but is the only way to close the underlying Level db.