Skip to content

Commit

Permalink
Add example blockstore examples
Browse files Browse the repository at this point in the history
Implement examples that show how to open a read-only blockstore, and a
read-write blockstore along with resumption from the same file.

The example surfaced a race condition where if the `AllKeysChan` is
partially consumed and file is closed then the gorutie started by chan
population causes the race condition. Locking on Close will make the
closure hang. Better solution is needed but choices are limited due to
`AllKeysChan` signature.

Fixes:
- #124
  • Loading branch information
masih committed Jul 16, 2021
1 parent e45d3b2 commit f138712
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
148 changes: 148 additions & 0 deletions v2/blockstore/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package blockstore_test

import (
"context"
"fmt"

blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-merkledag"
carv2 "github.com/ipld/go-car/v2"
"github.com/ipld/go-car/v2/blockstore"
)

const cidPrintCount = 5

// ExampleOpenReadOnly opens a read-only blockstore from a CARv1 file, and prints its root CIDs
// along with CID mapping to raw data size of blocks for first five sections in the CAR file.
func ExampleOpenReadOnly() {
// Open a new ReadOnly blockstore from a CARv1 file.
// Note, `OpenReadOnly` accepts bot CARv1 and CARv2 formats and transparently generate index
// in the background if necessary.
// This instance sets ZeroLengthSectionAsEOF option to treat zero sized sections in file as EOF.
robs, err := blockstore.OpenReadOnly("../testdata/sample-v1.car", carv2.ZeroLengthSectionAsEOF)
if err != nil {
panic(err)
}
defer robs.Close()

// Print root CIDs.
roots, err := robs.Roots()
if err != nil {
panic(err)
}
fmt.Printf("Contains %v root CID(s):\n", len(roots))
for _, r := range roots {
fmt.Printf("\t%v\n", r)
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Print the raw data size for the first 5 CIDs in the CAR file.
keysChan, err := robs.AllKeysChan(ctx)
if err != nil {
panic(err)
}
fmt.Printf("List of first %v CIDs and their raw data size:\n", cidPrintCount)
i := 1
for k := range keysChan {
if i > cidPrintCount {
cancel()
break
}
size, err := robs.GetSize(k)
if err != nil {
panic(err)
}
fmt.Printf("\t%v -> %v bytes\n", k, size)
i++
}

// Output:
// Contains 1 root CID(s):
// bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy
// List of first 5 CIDs and their raw data size:
// bafy2bzaced4ueelaegfs5fqu4tzsh6ywbbpfk3cxppupmxfdhbpbhzawfw5oy -> 821 bytes
// bafy2bzaceaycv7jhaegckatnncu5yugzkrnzeqsppzegufr35lroxxnsnpspu -> 1053 bytes
// bafy2bzaceb62wdepofqu34afqhbcn4a7jziwblt2ih5hhqqm6zitd3qpzhdp4 -> 1094 bytes
// bafy2bzaceb3utcspm5jqcdqpih3ztbaztv7yunzkiyfq7up7xmokpxemwgu5u -> 1051 bytes
// bafy2bzacedjwekyjresrwjqj4n2r5bnuuu3klncgjo2r3slsp6wgqb37sz4ck -> 821 bytes
}

// ExampleOpenReadWrite creates a read-write blockstore and puts
func ExampleOpenReadWrite() {
thisBlock := merkledag.NewRawNode([]byte("fish")).Block
thatBlock := merkledag.NewRawNode([]byte("lobster")).Block
andTheOtherBlock := merkledag.NewRawNode([]byte("barreleye")).Block

dest := "../testdata/sample-rw-bs-v2.car"
roots := []cid.Cid{thisBlock.Cid(), thatBlock.Cid(), andTheOtherBlock.Cid()}

rwbs, err := blockstore.OpenReadWrite(dest, roots, carv2.UseDataPadding(1413), carv2.UseIndexPadding(42))
if err != nil {
panic(err)
}
defer rwbs.Close()

// Put all blocks onto the blockstore.
blocks := []blocks.Block{thisBlock, thatBlock}
if err := rwbs.PutMany(blocks); err != nil {
panic(err)
}
fmt.Printf("Successfully wrote %v blocks into the blockstore.\n", len(blocks))

// Any blocks put can be read back using the same blockstore instance.
block, err := rwbs.Get(thatBlock.Cid())
if err != nil {
panic(err)
}
fmt.Printf("Read back block just put with raw value of `%v`.\n", string(block.RawData()))

// Finalize the blockstore to flush out the index and make a complete CARv2.
if err := rwbs.Finalize(); err != nil {
panic(err)
}

// Resume from the same file to add more blocks.
// Note the UseDataPadding and roots must match the values passed to the blockstore instance
// that created the original file. Otherwise, we cannot resume from the same file.
resumedRwbos, err := blockstore.OpenReadWrite(dest, roots, carv2.UseDataPadding(1413))
if err != nil {
panic(err)
}
defer resumedRwbos.Close()

// Put another block, appending it to the set of blocks that are written previously.
if err := resumedRwbos.Put(andTheOtherBlock); err != nil {
panic(err)
}

// Read back the the block put before resumption.
// Blocks previously put are present.
block, err = resumedRwbos.Get(thatBlock.Cid())
if err != nil {
panic(err)
}
fmt.Printf("Resumed blockstore contains blocks put previously with raw value of `%v`.\n", string(block.RawData()))

// Put an additional block to the CAR.
// Blocks put after resumption are also present.
block, err = resumedRwbos.Get(andTheOtherBlock.Cid())
if err != nil {
panic(err)
}
fmt.Printf("It also contains the block put after resumption with raw value of `%v`.\n", string(block.RawData()))

// Finalize the blockstore to flush out the index and make a complete CARv2.
// Note, Finalize must be called on an open ReadWrite blockstore to flush out a complete CARv2.
if err := resumedRwbos.Finalize(); err != nil {
panic(err)
}

// Output:
// Successfully wrote 2 blocks into the blockstore.
// Read back block just put with raw value of `lobster`.
// Resumed blockstore contains blocks put previously with raw value of `lobster`.
// It also contains the block put after resumption with raw value of `barreleye`.
}
Binary file added v2/testdata/sample-rw-bs-v2.car
Binary file not shown.

0 comments on commit f138712

Please sign in to comment.