From f5b22ffa59c2f2df8eaeb5b7adb953aa335b2481 Mon Sep 17 00:00:00 2001 From: Martin Hutchinson Date: Thu, 9 Nov 2023 11:16:47 +0000 Subject: [PATCH] A really dirty rip-out of the subtree revisions Just to confirm that removing this will actually work in practice. The tests pass, so now we need to design an upgrade mechanism. Probably forking this mysql driver / schema to be a mysql2 which is effectively a different driver. We'll also need to make some changes to this to support MariaDB. The 'REPLACE INTO' syntax is, I'm pretty sure, a MySQL specfic extension. There is a more standard format for doing this, but it involved passing in more '?' parameters which was already getting too wild for me with the '' expansion. Probably a solveable problem, but one for when I'm a bit less ill :-) --- storage/mysql/log_storage.go | 2 +- storage/mysql/schema/storage.sql | 3 +-- storage/mysql/tree_storage.go | 43 +++++++++++--------------------- 3 files changed, 16 insertions(+), 32 deletions(-) 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 {