github.com/bartle-stripe/trillian@v1.2.1/storage/mysql/queue.go (about) 1 // +build !batched_queue 2 3 // Copyright 2017 Google Inc. All Rights Reserved. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package mysql 18 19 import ( 20 "context" 21 "database/sql" 22 "errors" 23 "fmt" 24 "time" 25 26 "github.com/golang/glog" 27 "github.com/golang/protobuf/ptypes" 28 "github.com/google/trillian" 29 ) 30 31 const ( 32 // If this statement ORDER BY clause is changed refer to the comment in removeSequencedLeaves 33 selectQueuedLeavesSQL = `SELECT LeafIdentityHash,MerkleLeafHash,QueueTimestampNanos 34 FROM Unsequenced 35 WHERE TreeID=? 36 AND Bucket=0 37 AND QueueTimestampNanos<=? 38 ORDER BY QueueTimestampNanos,LeafIdentityHash ASC LIMIT ?` 39 insertUnsequencedEntrySQL = `INSERT INTO Unsequenced(TreeId,Bucket,LeafIdentityHash,MerkleLeafHash,QueueTimestampNanos) 40 VALUES(?,0,?,?,?)` 41 deleteUnsequencedSQL = "DELETE FROM Unsequenced WHERE TreeId=? AND Bucket=0 AND QueueTimestampNanos=? AND LeafIdentityHash=?" 42 ) 43 44 type dequeuedLeaf struct { 45 queueTimestampNanos int64 46 leafIdentityHash []byte 47 } 48 49 func dequeueInfo(leafIDHash []byte, queueTimestamp int64) dequeuedLeaf { 50 return dequeuedLeaf{queueTimestampNanos: queueTimestamp, leafIdentityHash: leafIDHash} 51 } 52 53 func (t *logTreeTX) dequeueLeaf(rows *sql.Rows) (*trillian.LogLeaf, dequeuedLeaf, error) { 54 var leafIDHash []byte 55 var merkleHash []byte 56 var queueTimestamp int64 57 58 err := rows.Scan(&leafIDHash, &merkleHash, &queueTimestamp) 59 if err != nil { 60 glog.Warningf("Error scanning work rows: %s", err) 61 return nil, dequeuedLeaf{}, err 62 } 63 64 // Note: the LeafData and ExtraData being nil here is OK as this is only used by the 65 // sequencer. The sequencer only writes to the SequencedLeafData table and the client 66 // supplied data was already written to LeafData as part of queueing the leaf. 67 queueTimestampProto, err := ptypes.TimestampProto(time.Unix(0, queueTimestamp)) 68 if err != nil { 69 return nil, dequeuedLeaf{}, fmt.Errorf("got invalid queue timestamp: %v", err) 70 } 71 leaf := &trillian.LogLeaf{ 72 LeafIdentityHash: leafIDHash, 73 MerkleLeafHash: merkleHash, 74 QueueTimestamp: queueTimestampProto, 75 } 76 return leaf, dequeueInfo(leafIDHash, queueTimestamp), nil 77 } 78 79 func queueArgs(treeID int64, identityHash []byte, queueTimestamp time.Time) []interface{} { 80 return []interface{}{queueTimestamp.UnixNano()} 81 } 82 83 func (t *logTreeTX) UpdateSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf) error { 84 for _, leaf := range leaves { 85 // This should fail on insert but catch it early 86 if len(leaf.LeafIdentityHash) != t.hashSizeBytes { 87 return errors.New("sequenced leaf has incorrect hash size") 88 } 89 90 iTimestamp, err := ptypes.Timestamp(leaf.IntegrateTimestamp) 91 if err != nil { 92 return fmt.Errorf("got invalid integrate timestamp: %v", err) 93 } 94 _, err = t.tx.ExecContext( 95 ctx, 96 insertSequencedLeafSQL+valuesPlaceholder5, 97 t.treeID, 98 leaf.LeafIdentityHash, 99 leaf.MerkleLeafHash, 100 leaf.LeafIndex, 101 iTimestamp.UnixNano()) 102 if err != nil { 103 glog.Warningf("Failed to update sequenced leaves: %s", err) 104 return err 105 } 106 } 107 108 return nil 109 } 110 111 // removeSequencedLeaves removes the passed in leaves slice (which may be 112 // modified as part of the operation). 113 func (t *logTreeTX) removeSequencedLeaves(ctx context.Context, leaves []dequeuedLeaf) error { 114 // Don't need to re-sort because the query ordered by leaf hash. If that changes because 115 // the query is expensive then the sort will need to be done here. See comment in 116 // QueueLeaves. 117 stx, err := t.tx.PrepareContext(ctx, deleteUnsequencedSQL) 118 if err != nil { 119 glog.Warningf("Failed to prep delete statement for sequenced work: %v", err) 120 return err 121 } 122 for _, dql := range leaves { 123 result, err := stx.ExecContext(ctx, t.treeID, dql.queueTimestampNanos, dql.leafIdentityHash) 124 err = checkResultOkAndRowCountIs(result, err, int64(1)) 125 if err != nil { 126 return err 127 } 128 } 129 130 return nil 131 }