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

Close only waits for write transactions to finish #90

Closed
vansante opened this issue Mar 28, 2018 · 5 comments
Closed

Close only waits for write transactions to finish #90

vansante opened this issue Mar 28, 2018 · 5 comments

Comments

@vansante
Copy link
Contributor

Hi,

This is a followup on: #80

I have been testing with closing BoltDB and ran into panic's in view transactions after calling Close(). After looking at the Close() code I think I can conclude that it waits for write transactions to finish by locking on db.rwlock but it does not wait for read transactions.

The only read lock a view transaction keeps open is the db.mmaplock, but the Close() function also only requires a read lock on that. So it does not wait for them. Am I correct in this conclusion?

If true, I think the documentation should be amended to reflect that it does not wait for read transactions, or the Close() function should acquire a full lock on db.mmaplock so that it also waits for read transactions. That said, I am not familiar enough with BoltDB's code to know what other consequences that might have.

I'm willing to submit a PR if you tell me which option you prefer :)

@vansante
Copy link
Contributor Author

vansante commented Mar 29, 2018

To illustrate the problem, I created a small test script:

package main

import (
	"log"
	"time"
	bolt "github.com/coreos/bbolt"
)

func main() {
	log.Println("Opening DB (update)")
	db, err := bolt.Open("my.db", 0600, nil)
	if err != nil {
		log.Fatal(err)
	}

	start := make(chan bool)
	go func() {
		db.Update(func(tx *bolt.Tx) error {
			close(start)
			time.Sleep(time.Second * 4)

			bk, err := tx.CreateBucket([]byte("NewBucket"))
			if err != nil {
				return err
			}

			return bk.Put([]byte("newKey"), []byte("newVal"))
		})
	}()

	// Wait until the transaction has begun
	<-start
	log.Println("Closing DB (update)")
	// This will block on the update transaction
	db.Close()
	log.Println("Closed DB (update)")

	log.Println("Opening DB (view)")
	db, err = bolt.Open("my.db", 0600, nil)
	if err != nil {
		log.Fatal(err)
	}

	start = make(chan bool)
	go func() {
		db.View(func(tx *bolt.Tx) error {
			close(start)
			time.Sleep(time.Second * 4)

			bk := tx.Bucket([]byte("NewBucket"))
			bk.Get([]byte("newKey"))

			bk = tx.Bucket([]byte("AnotherNotExistingBucket"))
			return nil
		})
	}()

	// Wait until the transaction has begun
	<-start
	log.Println("Closing DB (view)")
	// This will not block on the view transaction, and the view transaction could panic
	db.Close()
	log.Println("Closed DB (view)")
}

This gives the following output:

2018/03/29 14:17:19 Opening DB (update)
2018/03/29 14:17:19 Closing DB (update)
2018/03/29 14:17:23 Closed DB (update)
2018/03/29 14:17:23 Opening DB (view)
2018/03/29 14:17:23 Closing DB (view)
2018/03/29 14:17:23 Closed DB (view)

As you can clearly see there is a 4 second wait period in the closing of the update transaction, but there is not for the view transaction.

@xiang90
Copy link
Contributor

xiang90 commented Mar 29, 2018

probably it should wait for all transactions, not just writes.

@vansante
Copy link
Contributor Author

vansante commented Apr 9, 2018

@xiang90 Do you need anything more for this issue?

@vtolstov
Copy link

ping...

@vansante
Copy link
Contributor Author

The PR is merged, so Im closing this :)

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

3 participants