瀏覽代碼

Use persistent connection for links database

Add close method to Runtime and Server to make sure
that any underlying connections are cleaned up
Michael Crosby 11 年之前
父節點
當前提交
7c08aeeba4
共有 6 個文件被更改,包括 156 次插入177 次删除
  1. 10 4
      docker/docker.go
  2. 70 125
      gograph/gograph.go
  3. 49 46
      gograph/gograph_test.go
  4. 20 1
      runtime.go
  5. 3 1
      runtime_test.go
  6. 4 0
      server.go

+ 10 - 4
docker/docker.go

@@ -148,18 +148,22 @@ func daemon(config *docker.DaemonConfig) error {
 	}
 	}
 	defer removePidFile(config.Pidfile)
 	defer removePidFile(config.Pidfile)
 
 
+	server, err := docker.NewServer(config)
+	if err != nil {
+		return err
+	}
+	defer server.Close()
+
 	c := make(chan os.Signal, 1)
 	c := make(chan os.Signal, 1)
 	signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
 	signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
 	go func() {
 	go func() {
 		sig := <-c
 		sig := <-c
 		log.Printf("Received signal '%v', exiting\n", sig)
 		log.Printf("Received signal '%v', exiting\n", sig)
+		server.Close()
 		removePidFile(config.Pidfile)
 		removePidFile(config.Pidfile)
 		os.Exit(0)
 		os.Exit(0)
 	}()
 	}()
-	server, err := docker.NewServer(config)
-	if err != nil {
-		return err
-	}
+
 	chErrors := make(chan error, len(config.ProtoAddresses))
 	chErrors := make(chan error, len(config.ProtoAddresses))
 	for _, protoAddr := range config.ProtoAddresses {
 	for _, protoAddr := range config.ProtoAddresses {
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
@@ -170,6 +174,8 @@ func daemon(config *docker.DaemonConfig) error {
 				log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
 				log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
 			}
 			}
 		} else {
 		} else {
+			server.Close()
+			removePidFile(config.Pidfile)
 			log.Fatal("Invalid protocol format.")
 			log.Fatal("Invalid protocol format.")
 		}
 		}
 		go func() {
 		go func() {

+ 70 - 125
gograph/gograph.go

@@ -1,10 +1,8 @@
 package gograph
 package gograph
 
 
 import (
 import (
-	_ "code.google.com/p/gosqlite/sqlite3"
 	"database/sql"
 	"database/sql"
 	"fmt"
 	"fmt"
-	"os"
 	"path"
 	"path"
 )
 )
 
 
@@ -25,7 +23,7 @@ const (
     `
     `
 
 
 	createEdgeIndices = `
 	createEdgeIndices = `
-    CREATE UNIQUE INDEX "name_parent_ix" ON "edge" (parent_id, name);
+    CREATE UNIQUE INDEX IF NOT EXISTS "name_parent_ix" ON "edge" (parent_id, name);
     `
     `
 )
 )
 
 
@@ -48,74 +46,71 @@ type WalkFunc func(fullPath string, entity *Entity) error
 
 
 // Graph database for storing entities and their relationships
 // Graph database for storing entities and their relationships
 type Database struct {
 type Database struct {
-	dbPath string
+	conn *sql.DB
 }
 }
 
 
 // Create a new graph database initialized with a root entity
 // Create a new graph database initialized with a root entity
-func NewDatabase(dbPath string) (*Database, error) {
-	db := &Database{dbPath}
-	if _, err := os.Stat(dbPath); err == nil {
-		return db, nil
+func NewDatabase(conn *sql.DB, init bool) (*Database, error) {
+	if conn == nil {
+		return nil, fmt.Errorf("Database connection cannot be nil")
 	}
 	}
-	conn, err := db.openConn()
-	if err != nil {
-		return nil, err
-	}
-	defer conn.Close()
+	db := &Database{conn}
 
 
-	if _, err := conn.Exec(createEntityTable); err != nil {
-		return nil, err
-	}
-	if _, err := conn.Exec(createEdgeTable); err != nil {
-		return nil, err
-	}
-	if _, err := conn.Exec(createEdgeIndices); err != nil {
-		return nil, err
-	}
+	if init {
+		if _, err := conn.Exec(createEntityTable); err != nil {
+			return nil, err
+		}
+		if _, err := conn.Exec(createEdgeTable); err != nil {
+			return nil, err
+		}
+		if _, err := conn.Exec(createEdgeIndices); err != nil {
+			return nil, err
+		}
 
 
-	rollback := func() {
-		conn.Exec("ROLLBACK")
-	}
+		rollback := func() {
+			conn.Exec("ROLLBACK")
+		}
 
 
-	// Create root entities
-	if _, err := conn.Exec("BEGIN"); err != nil {
-		return nil, err
-	}
-	if _, err := conn.Exec("INSERT INTO entity (id) VALUES (?);", "0"); err != nil {
-		rollback()
-		return nil, err
-	}
+		// Create root entities
+		if _, err := conn.Exec("BEGIN"); err != nil {
+			return nil, err
+		}
+		if _, err := conn.Exec("INSERT INTO entity (id) VALUES (?);", "0"); err != nil {
+			rollback()
+			return nil, err
+		}
 
 
-	if _, err := conn.Exec("INSERT INTO edge (entity_id, name) VALUES(?,?);", "0", "/"); err != nil {
-		rollback()
-		return nil, err
-	}
+		if _, err := conn.Exec("INSERT INTO edge (entity_id, name) VALUES(?,?);", "0", "/"); err != nil {
+			rollback()
+			return nil, err
+		}
 
 
-	if _, err := conn.Exec("COMMIT"); err != nil {
-		return nil, err
+		if _, err := conn.Exec("COMMIT"); err != nil {
+			return nil, err
+		}
 	}
 	}
 	return db, nil
 	return db, nil
 }
 }
 
 
+// Close the underlying connection to the database
+func (db *Database) Close() error {
+	return db.conn.Close()
+}
+
 // Set the entity id for a given path
 // Set the entity id for a given path
 func (db *Database) Set(fullPath, id string) (*Entity, error) {
 func (db *Database) Set(fullPath, id string) (*Entity, error) {
-	conn, err := db.openConn()
-	if err != nil {
-		return nil, err
-	}
-	defer conn.Close()
 	// FIXME: is rollback implicit when closing the connection?
 	// FIXME: is rollback implicit when closing the connection?
 	rollback := func() {
 	rollback := func() {
-		conn.Exec("ROLLBACK")
+		db.conn.Exec("ROLLBACK")
 	}
 	}
 	// FIXME: use exclusive transactions to avoid race conditions
 	// FIXME: use exclusive transactions to avoid race conditions
-	if _, err := conn.Exec("BEGIN"); err != nil {
+	if _, err := db.conn.Exec("BEGIN"); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	var entityId string
 	var entityId string
-	if err := conn.QueryRow("SELECT id FROM entity WHERE id = ?;", id).Scan(&entityId); err != nil {
+	if err := db.conn.QueryRow("SELECT id FROM entity WHERE id = ?;", id).Scan(&entityId); err != nil {
 		if err == sql.ErrNoRows {
 		if err == sql.ErrNoRows {
-			if _, err := conn.Exec("INSERT INTO entity (id) VALUES(?);", id); err != nil {
+			if _, err := db.conn.Exec("INSERT INTO entity (id) VALUES(?);", id); err != nil {
 				rollback()
 				rollback()
 				return nil, err
 				return nil, err
 			}
 			}
@@ -127,19 +122,19 @@ func (db *Database) Set(fullPath, id string) (*Entity, error) {
 	e := &Entity{id}
 	e := &Entity{id}
 
 
 	parentPath, name := splitPath(fullPath)
 	parentPath, name := splitPath(fullPath)
-	if err := db.setEdge(conn, parentPath, name, e); err != nil {
+	if err := db.setEdge(parentPath, name, e); err != nil {
 		rollback()
 		rollback()
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	if _, err := conn.Exec("COMMIT"); err != nil {
+	if _, err := db.conn.Exec("COMMIT"); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	return e, nil
 	return e, nil
 }
 }
 
 
-func (db *Database) setEdge(conn *sql.DB, parentPath, name string, e *Entity) error {
-	parent, err := db.get(conn, parentPath)
+func (db *Database) setEdge(parentPath, name string, e *Entity) error {
+	parent, err := db.get(parentPath)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -147,7 +142,7 @@ func (db *Database) setEdge(conn *sql.DB, parentPath, name string, e *Entity) er
 		return fmt.Errorf("Cannot set self as child")
 		return fmt.Errorf("Cannot set self as child")
 	}
 	}
 
 
-	if _, err := conn.Exec("INSERT INTO edge (parent_id, name, entity_id) VALUES (?,?,?);", parent.id, name, e.id); err != nil {
+	if _, err := db.conn.Exec("INSERT INTO edge (parent_id, name, entity_id) VALUES (?,?,?);", parent.id, name, e.id); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -162,20 +157,14 @@ func (db *Database) RootEntity() *Entity {
 
 
 // Return the entity for a given path
 // Return the entity for a given path
 func (db *Database) Get(name string) *Entity {
 func (db *Database) Get(name string) *Entity {
-	conn, err := db.openConn()
-	if err != nil {
-		return nil
-	}
-	defer conn.Close()
-
-	e, err := db.get(conn, name)
+	e, err := db.get(name)
 	if err != nil {
 	if err != nil {
 		return nil
 		return nil
 	}
 	}
 	return e
 	return e
 }
 }
 
 
-func (db *Database) get(conn *sql.DB, name string) (*Entity, error) {
+func (db *Database) get(name string) (*Entity, error) {
 	e := db.RootEntity()
 	e := db.RootEntity()
 	// We always know the root name so return it if
 	// We always know the root name so return it if
 	// it is requested
 	// it is requested
@@ -187,7 +176,7 @@ func (db *Database) get(conn *sql.DB, name string) (*Entity, error) {
 	for i := 1; i < len(parts); i++ {
 	for i := 1; i < len(parts); i++ {
 		p := parts[i]
 		p := parts[i]
 
 
-		next := db.child(conn, e, p)
+		next := db.child(e, p)
 		if next == nil {
 		if next == nil {
 			return nil, fmt.Errorf("Cannot find child")
 			return nil, fmt.Errorf("Cannot find child")
 		}
 		}
@@ -201,26 +190,14 @@ func (db *Database) get(conn *sql.DB, name string) (*Entity, error) {
 // The key will be the full path of the entity
 // The key will be the full path of the entity
 func (db *Database) List(name string, depth int) Entities {
 func (db *Database) List(name string, depth int) Entities {
 	out := Entities{}
 	out := Entities{}
-	conn, err := db.openConn()
-	if err != nil {
-		return out
-	}
-	defer conn.Close()
-
-	for c := range db.children(conn, name, depth) {
+	for c := range db.children(name, depth) {
 		out[c.FullPath] = c.Entity
 		out[c.FullPath] = c.Entity
 	}
 	}
 	return out
 	return out
 }
 }
 
 
 func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
 func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
-	conn, err := db.openConn()
-	if err != nil {
-		return err
-	}
-	defer conn.Close()
-
-	for c := range db.children(conn, name, depth) {
+	for c := range db.children(name, depth) {
 		if err := walkFunc(c.FullPath, c.Entity); err != nil {
 		if err := walkFunc(c.FullPath, c.Entity); err != nil {
 			return err
 			return err
 		}
 		}
@@ -230,14 +207,8 @@ func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
 
 
 // Return the refrence count for a specified id
 // Return the refrence count for a specified id
 func (db *Database) Refs(id string) int {
 func (db *Database) Refs(id string) int {
-	conn, err := db.openConn()
-	if err != nil {
-		return -1
-	}
-	defer conn.Close()
-
 	var count int
 	var count int
-	if err := conn.QueryRow("SELECT COUNT(*) FROM edge WHERE entity_id = ?;", id).Scan(&count); err != nil {
+	if err := db.conn.QueryRow("SELECT COUNT(*) FROM edge WHERE entity_id = ?;", id).Scan(&count); err != nil {
 		return 0
 		return 0
 	}
 	}
 	return count
 	return count
@@ -246,13 +217,8 @@ func (db *Database) Refs(id string) int {
 // Return all the id's path references
 // Return all the id's path references
 func (db *Database) RefPaths(id string) Edges {
 func (db *Database) RefPaths(id string) Edges {
 	refs := Edges{}
 	refs := Edges{}
-	conn, err := db.openConn()
-	if err != nil {
-		return refs
-	}
-	defer conn.Close()
 
 
-	rows, err := conn.Query("SELECT name, parent_id FROM edge WHERE entity_id = ?;", id)
+	rows, err := db.conn.Query("SELECT name, parent_id FROM edge WHERE entity_id = ?;", id)
 	if err != nil {
 	if err != nil {
 		return refs
 		return refs
 	}
 	}
@@ -278,19 +244,14 @@ func (db *Database) Delete(name string) error {
 	if name == "/" {
 	if name == "/" {
 		return fmt.Errorf("Cannot delete root entity")
 		return fmt.Errorf("Cannot delete root entity")
 	}
 	}
-	conn, err := db.openConn()
-	if err != nil {
-		return err
-	}
-	defer conn.Close()
 
 
 	parentPath, n := splitPath(name)
 	parentPath, n := splitPath(name)
-	parent, err := db.get(conn, parentPath)
+	parent, err := db.get(parentPath)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if _, err := conn.Exec("DELETE FROM edge WHERE parent_id = ? AND name LIKE ?;", parent.id, n+"%"); err != nil {
+	if _, err := db.conn.Exec("DELETE FROM edge WHERE parent_id = ? AND name LIKE ?;", parent.id, n+"%"); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -300,22 +261,16 @@ func (db *Database) Delete(name string) error {
 // Walk the graph to make sure all references to the entity
 // Walk the graph to make sure all references to the entity
 // are removed and return the number of references removed
 // are removed and return the number of references removed
 func (db *Database) Purge(id string) (int, error) {
 func (db *Database) Purge(id string) (int, error) {
-	conn, err := db.openConn()
-	if err != nil {
-		return -1, err
-	}
-	defer conn.Close()
-
 	rollback := func() {
 	rollback := func() {
-		conn.Exec("ROLLBACK")
+		db.conn.Exec("ROLLBACK")
 	}
 	}
 
 
-	if _, err := conn.Exec("BEGIN"); err != nil {
+	if _, err := db.conn.Exec("BEGIN"); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
 
 
 	// Delete all edges
 	// Delete all edges
-	rows, err := conn.Exec("DELETE FROM edge WHERE entity_id = ?;", id)
+	rows, err := db.conn.Exec("DELETE FROM edge WHERE entity_id = ?;", id)
 	if err != nil {
 	if err != nil {
 		rollback()
 		rollback()
 		return -1, err
 		return -1, err
@@ -327,12 +282,12 @@ func (db *Database) Purge(id string) (int, error) {
 	}
 	}
 
 
 	// Delete entity
 	// Delete entity
-	if _, err := conn.Exec("DELETE FROM entity where id = ?;", id); err != nil {
+	if _, err := db.conn.Exec("DELETE FROM entity where id = ?;", id); err != nil {
 		rollback()
 		rollback()
 		return -1, err
 		return -1, err
 	}
 	}
 
 
-	if _, err := conn.Exec("COMMIT"); err != nil {
+	if _, err := db.conn.Exec("COMMIT"); err != nil {
 		return -1, err
 		return -1, err
 	}
 	}
 	return int(changes), nil
 	return int(changes), nil
@@ -347,18 +302,12 @@ func (db *Database) Rename(currentName, newName string) error {
 		return fmt.Errorf("Cannot rename when root paths do not match %s != %s", parentPath, newParentPath)
 		return fmt.Errorf("Cannot rename when root paths do not match %s != %s", parentPath, newParentPath)
 	}
 	}
 
 
-	conn, err := db.openConn()
+	parent, err := db.get(parentPath)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	defer conn.Close()
 
 
-	parent, err := db.get(conn, parentPath)
-	if err != nil {
-		return err
-	}
-
-	rows, err := conn.Exec("UPDATE edge SET name = ? WHERE parent_id = ? AND name LIKE ?;", newEdgeName, parent.id, name+"%")
+	rows, err := db.conn.Exec("UPDATE edge SET name = ? WHERE parent_id = ? AND name LIKE ?;", newEdgeName, parent.id, name+"%")
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -379,16 +328,16 @@ type WalkMeta struct {
 	Edge     *Edge
 	Edge     *Edge
 }
 }
 
 
-func (db *Database) children(conn *sql.DB, name string, depth int) <-chan WalkMeta {
+func (db *Database) children(name string, depth int) <-chan WalkMeta {
 	out := make(chan WalkMeta)
 	out := make(chan WalkMeta)
-	e, err := db.get(conn, name)
+	e, err := db.get(name)
 	if err != nil {
 	if err != nil {
 		close(out)
 		close(out)
 		return out
 		return out
 	}
 	}
 
 
 	go func() {
 	go func() {
-		rows, err := conn.Query("SELECT entity_id, name FROM edge where parent_id = ?;", e.id)
+		rows, err := db.conn.Query("SELECT entity_id, name FROM edge where parent_id = ?;", e.id)
 		if err != nil {
 		if err != nil {
 			close(out)
 			close(out)
 		}
 		}
@@ -422,7 +371,7 @@ func (db *Database) children(conn *sql.DB, name string, depth int) <-chan WalkMe
 			if depth != -1 {
 			if depth != -1 {
 				nDepth -= 1
 				nDepth -= 1
 			}
 			}
-			sc := db.children(conn, meta.FullPath, nDepth)
+			sc := db.children(meta.FullPath, nDepth)
 			for c := range sc {
 			for c := range sc {
 				out <- c
 				out <- c
 			}
 			}
@@ -433,18 +382,14 @@ func (db *Database) children(conn *sql.DB, name string, depth int) <-chan WalkMe
 }
 }
 
 
 // Return the entity based on the parent path and name
 // Return the entity based on the parent path and name
-func (db *Database) child(conn *sql.DB, parent *Entity, name string) *Entity {
+func (db *Database) child(parent *Entity, name string) *Entity {
 	var id string
 	var id string
-	if err := conn.QueryRow("SELECT entity_id FROM edge WHERE parent_id = ? AND name LIKE ?;", parent.id, name+"%").Scan(&id); err != nil {
+	if err := db.conn.QueryRow("SELECT entity_id FROM edge WHERE parent_id = ? AND name LIKE ?;", parent.id, name+"%").Scan(&id); err != nil {
 		return nil
 		return nil
 	}
 	}
 	return &Entity{id}
 	return &Entity{id}
 }
 }
 
 
-func (db *Database) openConn() (*sql.DB, error) {
-	return sql.Open("sqlite3", db.dbPath)
-}
-
 // Return the id used to reference this entity
 // Return the id used to reference this entity
 func (e *Entity) ID() string {
 func (e *Entity) ID() string {
 	return e.id
 	return e.id

+ 49 - 46
gograph/gograph_test.go

@@ -1,35 +1,39 @@
 package gograph
 package gograph
 
 
 import (
 import (
+	_ "code.google.com/p/gosqlite/sqlite3"
+	"database/sql"
 	"os"
 	"os"
 	"path"
 	"path"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
 )
 )
 
 
-func newTestDb(t *testing.T) *Database {
-	db, err := NewDatabase(path.Join(os.TempDir(), "sqlite.db"))
+func newTestDb(t *testing.T) (*Database, string) {
+	p := path.Join(os.TempDir(), "sqlite.db")
+	conn, err := sql.Open("sqlite3", p)
+	db, err := NewDatabase(conn, true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	return db
+	return db, p
 }
 }
 
 
-func destroyTestDb(db *Database) {
-	os.Remove(db.dbPath)
+func destroyTestDb(dbPath string) {
+	os.Remove(dbPath)
 }
 }
 
 
 func TestNewDatabase(t *testing.T) {
 func TestNewDatabase(t *testing.T) {
-	db := newTestDb(t)
+	db, dbpath := newTestDb(t)
 	if db == nil {
 	if db == nil {
 		t.Fatal("Database should not be nil")
 		t.Fatal("Database should not be nil")
 	}
 	}
-	defer destroyTestDb(db)
+	defer destroyTestDb(dbpath)
 }
 }
 
 
 func TestCreateRootEnity(t *testing.T) {
 func TestCreateRootEnity(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 	root := db.RootEntity()
 	root := db.RootEntity()
 	if root == nil {
 	if root == nil {
 		t.Fatal("Root entity should not be nil")
 		t.Fatal("Root entity should not be nil")
@@ -37,8 +41,8 @@ func TestCreateRootEnity(t *testing.T) {
 }
 }
 
 
 func TestGetRootEntity(t *testing.T) {
 func TestGetRootEntity(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	e := db.Get("/")
 	e := db.Get("/")
 	if e == nil {
 	if e == nil {
@@ -50,8 +54,8 @@ func TestGetRootEntity(t *testing.T) {
 }
 }
 
 
 func TestSetEntityWithDifferentName(t *testing.T) {
 func TestSetEntityWithDifferentName(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	db.Set("/test", "1")
 	db.Set("/test", "1")
 	if _, err := db.Set("/other", "1"); err != nil {
 	if _, err := db.Set("/other", "1"); err != nil {
@@ -60,8 +64,8 @@ func TestSetEntityWithDifferentName(t *testing.T) {
 }
 }
 
 
 func TestSetDuplicateEntity(t *testing.T) {
 func TestSetDuplicateEntity(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	if _, err := db.Set("/foo", "42"); err != nil {
 	if _, err := db.Set("/foo", "42"); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -72,8 +76,8 @@ func TestSetDuplicateEntity(t *testing.T) {
 }
 }
 
 
 func TestCreateChild(t *testing.T) {
 func TestCreateChild(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	child, err := db.Set("/db", "1")
 	child, err := db.Set("/db", "1")
 	if err != nil {
 	if err != nil {
@@ -88,8 +92,8 @@ func TestCreateChild(t *testing.T) {
 }
 }
 
 
 func TestListAllRootChildren(t *testing.T) {
 func TestListAllRootChildren(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	for i := 1; i < 6; i++ {
 	for i := 1; i < 6; i++ {
 		a := strconv.Itoa(i)
 		a := strconv.Itoa(i)
@@ -104,8 +108,8 @@ func TestListAllRootChildren(t *testing.T) {
 }
 }
 
 
 func TestListAllSubChildren(t *testing.T) {
 func TestListAllSubChildren(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	_, err := db.Set("/webapp", "1")
 	_, err := db.Set("/webapp", "1")
 	if err != nil {
 	if err != nil {
@@ -146,8 +150,8 @@ func TestListAllSubChildren(t *testing.T) {
 }
 }
 
 
 func TestAddSelfAsChild(t *testing.T) {
 func TestAddSelfAsChild(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	child, err := db.Set("/test", "1")
 	child, err := db.Set("/test", "1")
 	if err != nil {
 	if err != nil {
@@ -159,8 +163,8 @@ func TestAddSelfAsChild(t *testing.T) {
 }
 }
 
 
 func TestAddChildToNonExistantRoot(t *testing.T) {
 func TestAddChildToNonExistantRoot(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	if _, err := db.Set("/myapp", "1"); err != nil {
 	if _, err := db.Set("/myapp", "1"); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -172,8 +176,8 @@ func TestAddChildToNonExistantRoot(t *testing.T) {
 }
 }
 
 
 func TestWalkAll(t *testing.T) {
 func TestWalkAll(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 	_, err := db.Set("/webapp", "1")
 	_, err := db.Set("/webapp", "1")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -218,8 +222,8 @@ func TestWalkAll(t *testing.T) {
 }
 }
 
 
 func TestGetEntityByPath(t *testing.T) {
 func TestGetEntityByPath(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 	_, err := db.Set("/webapp", "1")
 	_, err := db.Set("/webapp", "1")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -265,8 +269,8 @@ func TestGetEntityByPath(t *testing.T) {
 }
 }
 
 
 func TestEnitiesPaths(t *testing.T) {
 func TestEnitiesPaths(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 	_, err := db.Set("/webapp", "1")
 	_, err := db.Set("/webapp", "1")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -309,8 +313,8 @@ func TestEnitiesPaths(t *testing.T) {
 }
 }
 
 
 func TestDeleteRootEntity(t *testing.T) {
 func TestDeleteRootEntity(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	if err := db.Delete("/"); err == nil {
 	if err := db.Delete("/"); err == nil {
 		t.Fatal("Error should not be nil")
 		t.Fatal("Error should not be nil")
@@ -318,8 +322,8 @@ func TestDeleteRootEntity(t *testing.T) {
 }
 }
 
 
 func TestDeleteEntity(t *testing.T) {
 func TestDeleteEntity(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 	_, err := db.Set("/webapp", "1")
 	_, err := db.Set("/webapp", "1")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -365,8 +369,8 @@ func TestDeleteEntity(t *testing.T) {
 }
 }
 
 
 func TestCountRefs(t *testing.T) {
 func TestCountRefs(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	db.Set("/webapp", "1")
 	db.Set("/webapp", "1")
 
 
@@ -382,8 +386,8 @@ func TestCountRefs(t *testing.T) {
 }
 }
 
 
 func TestPurgeId(t *testing.T) {
 func TestPurgeId(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	db.Set("/webapp", "1")
 	db.Set("/webapp", "1")
 
 
@@ -404,8 +408,8 @@ func TestPurgeId(t *testing.T) {
 }
 }
 
 
 func TestRename(t *testing.T) {
 func TestRename(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	db.Set("/webapp", "1")
 	db.Set("/webapp", "1")
 
 
@@ -433,8 +437,8 @@ func TestRename(t *testing.T) {
 }
 }
 
 
 func TestCreateMultipleNames(t *testing.T) {
 func TestCreateMultipleNames(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	db.Set("/db", "1")
 	db.Set("/db", "1")
 	if _, err := db.Set("/myapp", "1"); err != nil {
 	if _, err := db.Set("/myapp", "1"); err != nil {
@@ -448,8 +452,8 @@ func TestCreateMultipleNames(t *testing.T) {
 }
 }
 
 
 func TestRefPaths(t *testing.T) {
 func TestRefPaths(t *testing.T) {
-	db := newTestDb(t)
-	defer destroyTestDb(db)
+	db, dbpath := newTestDb(t)
+	defer destroyTestDb(dbpath)
 
 
 	db.Set("/webapp", "1")
 	db.Set("/webapp", "1")
 
 
@@ -460,5 +464,4 @@ func TestRefPaths(t *testing.T) {
 	if len(refs) != 2 {
 	if len(refs) != 2 {
 		t.Fatalf("Expected reference count to be 2, got %d", len(refs))
 		t.Fatalf("Expected reference count to be 2, got %d", len(refs))
 	}
 	}
-
 }
 }

+ 20 - 1
runtime.go

@@ -1,7 +1,9 @@
 package docker
 package docker
 
 
 import (
 import (
+	_ "code.google.com/p/gosqlite/sqlite3"
 	"container/list"
 	"container/list"
+	"database/sql"
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker/gograph"
 	"github.com/dotcloud/docker/gograph"
 	"github.com/dotcloud/docker/utils"
 	"github.com/dotcloud/docker/utils"
@@ -593,7 +595,19 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	graph, err := gograph.NewDatabase(path.Join(config.GraphPath, "linkgraph.db"))
+	gographPath := path.Join(config.GraphPath, "linkgraph.db")
+	initDatabase := false
+	if _, err := os.Stat(gographPath); err != nil {
+		if os.IsNotExist(err) {
+			initDatabase = true
+		}
+		return nil, err
+	}
+	conn, err := sql.Open("sqlite3", gographPath)
+	if err != nil {
+		return nil, err
+	}
+	graph, err := gograph.NewDatabase(conn, initDatabase)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -617,6 +631,11 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
 	return runtime, nil
 	return runtime, nil
 }
 }
 
 
+func (runtime *Runtime) Close() error {
+	runtime.networkManager.Close()
+	return runtime.containerGraph.Close()
+}
+
 // History is a convenience type for storing a list of containers,
 // History is a convenience type for storing a list of containers,
 // ordered by creation date.
 // ordered by creation date.
 type History []*Container
 type History []*Container

+ 3 - 1
runtime_test.go

@@ -44,7 +44,9 @@ func nuke(runtime *Runtime) error {
 		}(container)
 		}(container)
 	}
 	}
 	wg.Wait()
 	wg.Wait()
-	runtime.networkManager.Close()
+	runtime.Close()
+
+	os.Remove(filepath.Join(runtime.config.GraphPath, "linkgraph.db"))
 	return os.RemoveAll(runtime.config.GraphPath)
 	return os.RemoveAll(runtime.config.GraphPath)
 }
 }
 
 

+ 4 - 0
server.go

@@ -24,6 +24,10 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+func (srv *Server) Close() error {
+	return srv.runtime.Close()
+}
+
 func (srv *Server) DockerVersion() APIVersion {
 func (srv *Server) DockerVersion() APIVersion {
 	return APIVersion{
 	return APIVersion{
 		Version:   VERSION,
 		Version:   VERSION,