From 1ecc099e6ef9046be6f11d48548e7d5e7ac4c33f Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Sun, 30 Apr 2017 01:16:50 +0200 Subject: introduced read-only mode --- src/hub/src/spreadspace.org/sfive-hub/s5hub.go | 3 +- src/hub/src/spreadspace.org/sfive/s5srv.go | 4 +- src/hub/src/spreadspace.org/sfive/s5store.go | 15 +++-- src/hub/src/spreadspace.org/sfive/s5store_test.go | 69 ++++++++++++++++------- 4 files changed, 62 insertions(+), 29 deletions(-) (limited to 'src/hub') diff --git a/src/hub/src/spreadspace.org/sfive-hub/s5hub.go b/src/hub/src/spreadspace.org/sfive-hub/s5hub.go index 2bb436f..4028f3f 100644 --- a/src/hub/src/spreadspace.org/sfive-hub/s5hub.go +++ b/src/hub/src/spreadspace.org/sfive-hub/s5hub.go @@ -45,6 +45,7 @@ var s5hl = log.New(os.Stderr, "[s5hub]\t", log.LstdFlags) func main() { db := flag.String("db", "/var/lib/sfive/db.bolt", "path to the database file") + readOnly := flag.Bool("read-only", false, "open database in read-only mode") pipe := flag.String("pipe", "/var/run/sfive/pipe", "path to the unix pipe for the pipeserver") ppipe := flag.String("pipegram", "/var/run/sfive/pipegram", "path to the unix datagram pipe for the pipeserver") startPipe := flag.Bool("start-pipe-server", true, "start a connection oriented pipe server; see option pipe") @@ -68,7 +69,7 @@ func main() { return } - srv, err := sfive.NewServer(*db) + srv, err := sfive.NewServer(*db, *readOnly) if err != nil { s5hl.Fatalf(err.Error()) } diff --git a/src/hub/src/spreadspace.org/sfive/s5srv.go b/src/hub/src/spreadspace.org/sfive/s5srv.go index 8db3c7e..23429a9 100644 --- a/src/hub/src/spreadspace.org/sfive/s5srv.go +++ b/src/hub/src/spreadspace.org/sfive/s5srv.go @@ -158,10 +158,10 @@ func (srv Server) Close() { s5l.Printf("server: finished\n") } -func NewServer(dbPath string) (server *Server, err error) { +func NewServer(dbPath string, readOnly bool) (server *Server, err error) { // TODO read configuration and create instance with correct settings server = new(Server) - server.store, err = NewStore(dbPath) + server.store, err = NewStore(dbPath, readOnly) if err != nil { return } diff --git a/src/hub/src/spreadspace.org/sfive/s5store.go b/src/hub/src/spreadspace.org/sfive/s5store.go index ea114c7..d2a0734 100644 --- a/src/hub/src/spreadspace.org/sfive/s5store.go +++ b/src/hub/src/spreadspace.org/sfive/s5store.go @@ -59,15 +59,15 @@ type Store struct { db *bolt.DB } -func checkDb(dbPath string) (db *bolt.DB, version int, hubUuid string, err error) { +func checkDb(dbPath string, readOnly bool) (db *bolt.DB, version int, hubUuid string, err error) { if _, err = os.Stat(dbPath); err != nil { if os.IsNotExist(err) { err = nil } return } - - db, err = bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second}) + opts := &bolt.Options{Timeout: 100 * time.Millisecond, ReadOnly: readOnly} + db, err = bolt.Open(dbPath, 0600, opts) if err != nil { return } @@ -106,7 +106,7 @@ func checkDb(dbPath string) (db *bolt.DB, version int, hubUuid string, err error } func initDb(dbPath string) (db *bolt.DB, version int, hubUuid string, err error) { - db, err = bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second}) + db, err = bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 100 * time.Millisecond}) if err != nil { return } @@ -484,8 +484,8 @@ func (st Store) GetStoreId() string { return st.hubUuid } -func NewStore(dbPath string) (Store, error) { - db, version, hubid, err := checkDb(dbPath) +func NewStore(dbPath string, readOnly bool) (Store, error) { + db, version, hubid, err := checkDb(dbPath, readOnly) if err != nil { return Store{}, err } @@ -493,6 +493,9 @@ func NewStore(dbPath string) (Store, error) { if db != nil { s5l.Printf("store: opened (UUID: %s)", hubid) } else { + if readOnly { + return Store{}, errors.New("store: failed to open, requested read-only mode but file does not exist.") + } db, version, hubid, err = initDb(dbPath) if err != nil { return Store{}, err diff --git a/src/hub/src/spreadspace.org/sfive/s5store_test.go b/src/hub/src/spreadspace.org/sfive/s5store_test.go index 02ab727..bda0a5e 100644 --- a/src/hub/src/spreadspace.org/sfive/s5store_test.go +++ b/src/hub/src/spreadspace.org/sfive/s5store_test.go @@ -64,7 +64,7 @@ func TestMain(m *testing.M) { func TestOpen(t *testing.T) { // non-existing directory - if _, err := NewStore("/nonexistend/db.bolt"); err == nil { + if _, err := NewStore("/nonexistend/db.bolt", false); err == nil { t.Fatalf("opening store in nonexisting directory should throw an error") } @@ -73,8 +73,8 @@ func TestOpen(t *testing.T) { if err := os.MkdirAll(__boltPath, 0700); err != nil { t.Fatalf("unexpected error: %v", err) } - if _, err := NewStore(__boltPath); err == nil { - t.Fatalf("opening store using a directory should throw an error: %v", err) + if _, err := NewStore(__boltPath, false); err == nil { + t.Fatalf("opening store using a directory should throw an error") } // exisitng but non-database file @@ -85,13 +85,13 @@ func TestOpen(t *testing.T) { io.WriteString(f, "this is not a bolt db.") f.Close() } - if _, err := NewStore(__boltPath); err == nil { - t.Fatalf("opening store using a invalid database should throw an error: %v", err) + if _, err := NewStore(__boltPath, false); err == nil { + t.Fatalf("opening store using a invalid database should throw an error") } // bolt db with wrong layout os.Remove(__boltPath) - if db, err := bolt.Open(__boltPath, 0600, &bolt.Options{Timeout: 1 * time.Second}); err != nil { + if db, err := bolt.Open(__boltPath, 0600, &bolt.Options{Timeout: 100 * time.Millisecond}); err != nil { t.Fatalf("unexpected error: %v", err) } else { err = db.Update(func(tx *bolt.Tx) error { @@ -107,13 +107,13 @@ func TestOpen(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } - if _, err := NewStore(__boltPath); err == nil { - t.Fatalf("opening store using a invalid database should throw an error: %v", err) + if _, err := NewStore(__boltPath, false); err == nil { + t.Fatalf("opening store using a invalid database should throw an error") } // bolt db with wrong version os.Remove(__boltPath) - if db, err := bolt.Open(__boltPath, 0600, &bolt.Options{Timeout: 1 * time.Second}); err != nil { + if db, err := bolt.Open(__boltPath, 0600, &bolt.Options{Timeout: 100 * time.Millisecond}); err != nil { t.Fatalf("unexpected error: %v", err) } else { err = db.Update(func(tx *bolt.Tx) error { @@ -134,13 +134,13 @@ func TestOpen(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } - if _, err := NewStore(__boltPath); err == nil { - t.Fatalf("opening store with the wrong database version should throw an error: %v", err) + if _, err := NewStore(__boltPath, false); err == nil { + t.Fatalf("opening store with the wrong database version should throw an error") } // bolt db empty UUID os.Remove(__boltPath) - if db, err := bolt.Open(__boltPath, 0600, &bolt.Options{Timeout: 1 * time.Second}); err != nil { + if db, err := bolt.Open(__boltPath, 0600, &bolt.Options{Timeout: 100 * time.Millisecond}); err != nil { t.Fatalf("unexpected error: %v", err) } else { err = db.Update(func(tx *bolt.Tx) error { @@ -165,32 +165,36 @@ func TestOpen(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } - if _, err := NewStore(__boltPath); err == nil { - t.Fatalf("opening store with empty UUID should throw an error: %v", err) + if _, err := NewStore(__boltPath, false); err == nil { + t.Fatalf("opening store with empty UUID should throw an error") } // create new bolt-db and reopen it os.Remove(__boltPath) - store, err := NewStore(__boltPath) + store, err := NewStore(__boltPath, false) if err != nil { t.Fatalf("creating new store failed: %v", err) } createdUuid := store.hubUuid store.Close() - store, err = NewStore(__boltPath) + store, err = NewStore(__boltPath, false) if err != nil { t.Fatalf("re-opening existing store failed: %v", err) } if createdUuid != store.hubUuid { t.Fatalf("UUID of opened store differs from the one previously generated: '%s' != '%s'", createdUuid, store.hubUuid) } + + if _, err := NewStore(__boltPath, false); err == nil { + t.Fatalf("opening already opened database should throw an error") + } store.Close() } // func TestAppend(t *testing.T) { // os.Remove(__boltPath) -// store, err := NewStore(__boltPath) +// store, err := NewStore(__boltPath, false) // if err != nil { // t.Fatalf("Failed to initialize: %v", err) // } @@ -210,7 +214,7 @@ func TestOpen(t *testing.T) { // func TestGetUpdatesAfter(t *testing.T) { // os.Remove(__boltPath) -// store, err := NewStore(__boltPath) +// store, err := NewStore(__boltPath, false) // if err != nil { // t.Fatalf("Failed to initialize: %v", err) // } @@ -231,6 +235,31 @@ func TestOpen(t *testing.T) { // t.Logf("got updates (err %v):\n%#v", err, res) // } +func TestReadOnly(t *testing.T) { + // create read-only db from not-existing file must fail + os.Remove(__boltPath) + if _, err := NewStore(__boltPath, true); err == nil { + t.Fatalf("creating a read-only database should throw an error") + } + + // open read-only db from existing file must succeed + store, err := NewStore(__boltPath, false) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + store.Close() + + store, err = NewStore(__boltPath, false) + if err != nil { + t.Fatalf("opening existing store in read-only mode failed: %v", err) + } + store.Close() + + // + // TODO: test append on read-only store + // +} + // func generateDataUpdateFull(n int) (data []DataUpdateFull) { // hostnames := []string{"streamer1", "streamer2"} // contents := []string{"av", "audio"} @@ -284,7 +313,7 @@ func TestOpen(t *testing.T) { // func BenchmarkAppendMany(b *testing.B) { // os.Remove(__boltPath) -// store, err := NewStore(__boltPath) +// store, err := NewStore(__boltPath, false) // if err != nil { // b.Fatalf("Failed to initialize: %v", err) // } @@ -300,7 +329,7 @@ func TestOpen(t *testing.T) { // func BenchmarkGetUpdatesAfter(b *testing.B) { // os.Remove(__boltPath) -// store, err := NewStore(__boltPath) +// store, err := NewStore(__boltPath, false) // if err != nil { // b.Fatalf("Failed to initialize: %v", err) // } -- cgit v1.2.3