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.