autobatch.go 2.16 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Package autobatch provides a go-datastore implementation that
// automatically batches together writes by holding puts in memory until
// a certain threshold is met.
package autobatch

import (
	ds "github.com/ipfs/go-datastore"
	dsq "github.com/ipfs/go-datastore/query"
)

// Datastore implements a go-datatsore.
type Datastore struct {
	child ds.Batching

	// TODO: discuss making ds.Batch implement the full ds.Datastore interface
16
	buffer           map[ds.Key][]byte
17 18 19 20 21 22 23 24 25
	maxBufferEntries int
}

// NewAutoBatching returns a new datastore that automatically
// batches writes using the given Batching datastore. The size
// of the memory pool is given by size.
func NewAutoBatching(d ds.Batching, size int) *Datastore {
	return &Datastore{
		child:            d,
26
		buffer:           make(map[ds.Key][]byte),
27 28 29 30 31 32
		maxBufferEntries: size,
	}
}

// Delete deletes a key/value
func (d *Datastore) Delete(k ds.Key) error {
33
	_, found := d.buffer[k]
34 35
	delete(d.buffer, k)

36 37 38 39 40
	err := d.child.Delete(k)
	if found && err == ds.ErrNotFound {
		return nil
	}
	return err
41 42 43
}

// Get retrieves a value given a key.
44
func (d *Datastore) Get(k ds.Key) ([]byte, error) {
45 46 47 48 49 50 51 52 53
	val, ok := d.buffer[k]
	if ok {
		return val, nil
	}

	return d.child.Get(k)
}

// Put stores a key/value.
54
func (d *Datastore) Put(k ds.Key, val []byte) error {
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
	d.buffer[k] = val
	if len(d.buffer) > d.maxBufferEntries {
		return d.Flush()
	}
	return nil
}

// Flush flushes the current batch to the underlying datastore.
func (d *Datastore) Flush() error {
	b, err := d.child.Batch()
	if err != nil {
		return err
	}

	for k, v := range d.buffer {
		err := b.Put(k, v)
		if err != nil {
			return err
		}
	}
	// clear out buffer
76
	d.buffer = make(map[ds.Key][]byte)
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

	return b.Commit()
}

// Has checks if a key is stored.
func (d *Datastore) Has(k ds.Key) (bool, error) {
	_, ok := d.buffer[k]
	if ok {
		return true, nil
	}

	return d.child.Has(k)
}

// Query performs a query
func (d *Datastore) Query(q dsq.Query) (dsq.Results, error) {
	err := d.Flush()
	if err != nil {
		return nil, err
	}

	return d.child.Query(q)
}
100 101 102 103 104

// DiskUsage implements the PersistentDatastore interface.
func (d *Datastore) DiskUsage() (uint64, error) {
	return ds.DiskUsage(d.child)
}