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

Cursor.Last() returns nil for non-empty bucket #95

Closed
hrissan opened this issue Apr 9, 2018 · 3 comments
Closed

Cursor.Last() returns nil for non-empty bucket #95

hrissan opened this issue Apr 9, 2018 · 3 comments

Comments

@hrissan
Copy link

hrissan commented Apr 9, 2018

Ubuntu amd-64 go 1.9.2

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/coreos/bbolt"
)

var (
	testBucket = []byte("test")
)

func initDB(path string) (*bolt.DB, error) {
	db, err := bolt.Open(path, 0600, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		return nil, err
	}
	err = db.Update(func(tx *bolt.Tx) error {
		err := tx.DeleteBucket(testBucket)
		_, err = tx.CreateBucketIfNotExists(testBucket)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		db.Close()
		return nil, err
	}

	return db, nil
}

func formatHeight(a int) []byte {
	return []byte(fmt.Sprintf("%08d", a))
}

func delBackwards(cb *bolt.Bucket, index int) error {
	err := cb.Delete(formatHeight(index))
	if err != nil {
		return fmt.Errorf("delete failed: %v", err)
	}
	cc := cb.Cursor()
	k, _ := cc.Last()
	k2, _ := cc.First()
	if k == nil && k2 != nil {
		return fmt.Errorf("last and first are inconsistent at %d for %#v %#v", index, k, k2)
	}
	return nil
}

func test(n int, value string) {
	db, err := initDB("bad_bolt.db")
	if err != nil {
		log.Fatalf("failed to initialize database: %v", err)
	}
	defer db.Close()
	err = db.Update(func(tx *bolt.Tx) error {
		cb := tx.Bucket(testBucket)
		for i := 0; i < n; i += 1 {
			err := cb.Put(formatHeight(i), []byte(value))
			if err != nil {
				return fmt.Errorf("put failed failed: %v", err)
			}
		}
		return nil
	})
	if err != nil {
		log.Fatalf("first tx failed: %v", err)
	}
	err = db.Update(func(tx *bolt.Tx) error {
		cb := tx.Bucket(testBucket)
		for i := n - 1; i >= 0; i -= 1 {
			err := delBackwards(cb, i)
			if err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		log.Printf("second tx failed: %v", err)
	} else {
		log.Printf("%d %v OK", n, value)
	}
}

func main() {
	test(100, "0000000000000000")
	test(100, "00000000000000000")
}
@Hittorp
Copy link

Hittorp commented Apr 27, 2018

Is it enough to put n.rebalance() at the end of (n *node) del(key []byte) to fix this?

I am not sure about invariants that should provide cursor.Delete(). Is it ok to get an empty leaf page after delete a key?

@cenkalti
Copy link
Member

I have run the provided program hundreds of time but couldn't reproduce the issue mentioned. It's probably fixed due to this issue is quite old hence closing it. Feel free to reply and re-open if it is still valid.

@cenkalti cenkalti closed this as not planned Won't fix, can't repro, duplicate, stale May 16, 2023
@ahrtr
Copy link
Member

ahrtr commented May 16, 2023

FYI. This is a real issue, and was fixed in 1.3.7 in #341.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants