services/mounttable/btmtd: Add GC grace period

Add a grace period before garbage collecting newly created nodes. It
defaults to 1 minute, but is set to 0 for the tests.

Also, only create new rows if they don't already exist. If they exist,
return a concurrent access error.

Change-Id: I65850adcf96a3ac76e32b18d68658169b77ecd56
diff --git a/services/mounttable/btmtd/internal/bt.go b/services/mounttable/btmtd/internal/bt.go
index 64ec5e7..86cb9ce 100644
--- a/services/mounttable/btmtd/internal/bt.go
+++ b/services/mounttable/btmtd/internal/bt.go
@@ -27,6 +27,7 @@
 	"v.io/v23/security"
 	"v.io/v23/security/access"
 	v23mt "v.io/v23/services/mounttable"
+	"v.io/v23/verror"
 
 	"v.io/x/ref/lib/timekeeper"
 )
@@ -207,7 +208,7 @@
 	defer cancel()
 
 	clock := timekeeper.RealTime()
-	return b.nodeTbl.ReadRows(bctx, bigtable.InfiniteRange(""),
+	if err := b.nodeTbl.ReadRows(bctx, bigtable.InfiniteRange(""),
 		func(row bigtable.Row) bool {
 			n := nodeFromRow(ctx, b, row, clock)
 			if n.name == "" {
@@ -233,7 +234,19 @@
 			return true
 		},
 		bigtable.RowFilter(bigtable.LatestNFilter(1)),
-	)
+	); err != nil {
+		return err
+	}
+
+	c, err := b.Counters(ctx)
+	if err != nil {
+		return err
+	}
+	fmt.Printf("Counters:\n")
+	for k, v := range c {
+		fmt.Printf("%s=%d\n", k, v)
+	}
+	return nil
 }
 
 func (b *BigTable) CountRows(ctx *context.T) (int, error) {
@@ -341,8 +354,15 @@
 	mut.Set(metadataFamily, creatorColumn, ts, []byte(creator))
 	mut.Set(metadataFamily, permissionsColumn, bigtable.ServerTime, jsonPerms)
 	mut.Set(metadataFamily, versionColumn, bigtable.ServerTime, []byte(strconv.FormatUint(uint64(rand.Uint32()), 10)))
-	if err := b.apply(ctx, rowKey(name), mut); err != nil {
+
+	filter := bigtable.ChainFilters(bigtable.FamilyFilter(metadataFamily), bigtable.ColumnFilter(creatorColumn))
+	condMut := bigtable.NewCondMutation(filter, nil, mut)
+	var exists bool
+	if err := b.apply(ctx, rowKey(name), condMut, bigtable.GetCondMutationResult(&exists)); err != nil {
 		return err
 	}
+	if exists {
+		return verror.New(errConcurrentAccess, ctx, name)
+	}
 	return incrementCreatorNodeCount(ctx, b, creator, 1)
 }
diff --git a/services/mounttable/btmtd/internal/mounttable_test.go b/services/mounttable/btmtd/internal/mounttable_test.go
index aed1b76..b59784c 100644
--- a/services/mounttable/btmtd/internal/mounttable_test.go
+++ b/services/mounttable/btmtd/internal/mounttable_test.go
@@ -196,6 +196,7 @@
 	}
 	// Add mount table service.
 	internal.SetClock(clock)
+	internal.SetGcGracePeriod(0)
 	mt := internal.NewDispatcher(bt, nil)
 
 	// Start serving on a loopback address.
diff --git a/services/mounttable/btmtd/internal/node.go b/services/mounttable/btmtd/internal/node.go
index 3bc477a..efd3ae9 100644
--- a/services/mounttable/btmtd/internal/node.go
+++ b/services/mounttable/btmtd/internal/node.go
@@ -27,6 +27,15 @@
 	"v.io/x/ref/lib/timekeeper"
 )
 
+var gcGracePeriod = time.Minute
+
+// SetGcGracePeriod sets the grace period for garbage collecting newly created
+// nodes. Nodes are not eligible for garbage collection until after this time
+// has passed. This function exists only for testing purposes.
+func SetGcGracePeriod(p time.Duration) {
+	gcGracePeriod = p
+}
+
 type mtNode struct {
 	bt             *BigTable
 	name           string
@@ -231,6 +240,9 @@
 		if n.sticky || len(n.children) > 0 || len(n.servers) > 0 {
 			break
 		}
+		if time.Since(n.creationTime.Time()) < gcGracePeriod {
+			break
+		}
 		if err = n.delete(ctx, false); err != nil {
 			break
 		}