diff --git a/storage/mysql/log_storage.go b/storage/mysql/log_storage.go index 1c3795d5c6..e4d6432fba 100644 --- a/storage/mysql/log_storage.go +++ b/storage/mysql/log_storage.go @@ -324,7 +324,7 @@ type logTreeTX struct { func (t *logTreeTX) GetMerkleNodes(ctx context.Context, ids []compact.NodeID) ([]tree.Node, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() - return t.subtreeCache.GetNodes(ids, t.getSubtreesAtRev(ctx, t.readRev)) + return t.subtreeCache.GetNodes(ids, t.getSubtreesFunc(ctx)) } func (t *logTreeTX) DequeueLeaves(ctx context.Context, limit int, cutoffTime time.Time) ([]*trillian.LogLeaf, error) { diff --git a/storage/mysql/schema/storage.sql b/storage/mysql/schema/storage.sql index 8de7447ef2..6cd2ef5d12 100644 --- a/storage/mysql/schema/storage.sql +++ b/storage/mysql/schema/storage.sql @@ -40,10 +40,9 @@ CREATE TABLE IF NOT EXISTS Subtree( TreeId BIGINT NOT NULL, SubtreeId VARBINARY(255) NOT NULL, Nodes MEDIUMBLOB NOT NULL, - SubtreeRevision INTEGER NOT NULL, -- Key columns must be in ASC order in order to benefit from group-by/min-max -- optimization in MySQL. - PRIMARY KEY(TreeId, SubtreeId, SubtreeRevision), + PRIMARY KEY(TreeId, SubtreeId), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE ); diff --git a/storage/mysql/tree_storage.go b/storage/mysql/tree_storage.go index b09c3fcfe6..8207b722ff 100644 --- a/storage/mysql/tree_storage.go +++ b/storage/mysql/tree_storage.go @@ -34,24 +34,15 @@ import ( // These statements are fixed const ( - insertSubtreeMultiSQL = `INSERT INTO Subtree(TreeId, SubtreeId, Nodes, SubtreeRevision) ` + placeholderSQL + insertSubtreeMultiSQL = `REPLACE INTO Subtree(TreeId, SubtreeId, Nodes) ` + placeholderSQL insertTreeHeadSQL = `INSERT INTO TreeHead(TreeId,TreeHeadTimestamp,TreeSize,RootHash,TreeRevision,RootSignature) VALUES(?,?,?,?,?,?)` selectSubtreeSQL = ` - SELECT x.SubtreeId, x.MaxRevision, Subtree.Nodes - FROM ( - SELECT n.TreeId, n.SubtreeId, max(n.SubtreeRevision) AS MaxRevision - FROM Subtree n - WHERE n.SubtreeId IN (` + placeholderSQL + `) AND - n.TreeId = ? AND n.SubtreeRevision <= ? - GROUP BY n.TreeId, n.SubtreeId - ) AS x - INNER JOIN Subtree - ON Subtree.SubtreeId = x.SubtreeId - AND Subtree.SubtreeRevision = x.MaxRevision - AND Subtree.TreeId = x.TreeId - AND Subtree.TreeId = ?` + SELECT SubtreeId, Subtree.Nodes + FROM Subtree + WHERE Subtree.TreeId = ? + AND SubtreeId IN (` + placeholderSQL + `)` placeholderSQL = "" ) @@ -138,7 +129,7 @@ func (m *mySQLTreeStorage) getSubtreeStmt(ctx context.Context, num int) (*sql.St } func (m *mySQLTreeStorage) setSubtreeStmt(ctx context.Context, num int) (*sql.Stmt, error) { - return m.getStmt(ctx, insertSubtreeMultiSQL, num, "VALUES(?, ?, ?, ?)", "(?, ?, ?, ?)") + return m.getStmt(ctx, insertSubtreeMultiSQL, num, "VALUES(?, ?, ?)", "(?, ?, ?)") } func (m *mySQLTreeStorage) beginTreeTx(ctx context.Context, tree *trillian.Tree, hashSizeBytes int, subtreeCache *cache.SubtreeCache) (treeTX, error) { @@ -172,7 +163,7 @@ type treeTX struct { writeRevision int64 } -func (t *treeTX) getSubtrees(ctx context.Context, treeRevision int64, ids [][]byte) ([]*storagepb.SubtreeProto, error) { +func (t *treeTX) getSubtrees(ctx context.Context, ids [][]byte) ([]*storagepb.SubtreeProto, error) { klog.V(2).Infof("getSubtrees(len(ids)=%d)", len(ids)) klog.V(4).Infof("getSubtrees(") if len(ids) == 0 { @@ -190,7 +181,8 @@ func (t *treeTX) getSubtrees(ctx context.Context, treeRevision int64, ids [][]by } }() - args := make([]interface{}, 0, len(ids)+3) + args := make([]interface{}, 0, len(ids)+1) + args = append(args, t.treeID) // populate args with ids. for _, id := range ids { @@ -198,10 +190,6 @@ func (t *treeTX) getSubtrees(ctx context.Context, treeRevision int64, ids [][]by args = append(args, id) } - args = append(args, t.treeID) - args = append(args, treeRevision) - args = append(args, t.treeID) - rows, err := stx.QueryContext(ctx, args...) if err != nil { klog.Warningf("Failed to get merkle subtrees: %s", err) @@ -223,9 +211,8 @@ func (t *treeTX) getSubtrees(ctx context.Context, treeRevision int64, ids [][]by for rows.Next() { var subtreeIDBytes []byte - var subtreeRev int64 var nodesRaw []byte - if err := rows.Scan(&subtreeIDBytes, &subtreeRev, &nodesRaw); err != nil { + if err := rows.Scan(&subtreeIDBytes, &nodesRaw); err != nil { klog.Warningf("Failed to scan merkle subtree: %s", err) return nil, err } @@ -296,7 +283,6 @@ func (t *treeTX) storeSubtrees(ctx context.Context, subtrees []*storagepb.Subtre args = append(args, t.treeID) args = append(args, s.Prefix) args = append(args, subtreeBytes) - args = append(args, t.writeRevision) } tmpl, err := t.ts.setSubtreeStmt(ctx, len(subtrees)) @@ -340,18 +326,17 @@ func checkResultOkAndRowCountIs(res sql.Result, err error, count int64) error { return nil } -// getSubtreesAtRev returns a GetSubtreesFunc which reads at the passed in rev. -func (t *treeTX) getSubtreesAtRev(ctx context.Context, rev int64) cache.GetSubtreesFunc { +// getSubtrees returns a GetSubtreesFunc. +func (t *treeTX) getSubtreesFunc(ctx context.Context) cache.GetSubtreesFunc { return func(ids [][]byte) ([]*storagepb.SubtreeProto, error) { - return t.getSubtrees(ctx, rev, ids) + return t.getSubtrees(ctx, ids) } } func (t *treeTX) SetMerkleNodes(ctx context.Context, nodes []tree.Node) error { t.mu.Lock() defer t.mu.Unlock() - rev := t.writeRevision - 1 - return t.subtreeCache.SetNodes(nodes, t.getSubtreesAtRev(ctx, rev)) + return t.subtreeCache.SetNodes(nodes, t.getSubtreesFunc(ctx)) } func (t *treeTX) Commit(ctx context.Context) error {