github.com/decred/politeia@v1.4.0/politeiad/backendv2/tstorebe/plugins/ticketvote/fsck.go (about) 1 // Copyright (c) 2022 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package ticketvote 6 7 import ( 8 backend "github.com/decred/politeia/politeiad/backendv2" 9 "github.com/decred/politeia/politeiad/plugins/ticketvote" 10 ) 11 12 // fsck performs the ticketvote plugin file system check. The following checks 13 // are performed: 14 // 15 // 1. Rebuild the vote summaries cache. Records that have finished voting 16 // will have a vote summary saved to the cache. This cache is rebuilt 17 // from scratch. 18 // 19 // 2. Rebuild the vote inventory cache. All vetted records are included in the 20 // vote inventory cache. This cache is rebuilt from scratch. 21 // 22 // 3. Rebuild the runoff vote submissions cache. The submissions cache contains 23 // the list of runoff vote parent records and all of their runoff vote 24 // submissions. This cache is built from scratch. 25 func (p *ticketVotePlugin) fsck(tokens [][]byte) error { 26 log.Infof("Starting ticketvote fsck for %v records", len(tokens)) 27 28 // Filter out the vetted records. The ticketvote plugin 29 // commands can only be run on vetted records. 30 vetted := make([][]byte, 0, len(tokens)) 31 for _, token := range tokens { 32 state, err := p.tstore.RecordState(token) 33 if err != nil { 34 return err 35 } 36 if state == backend.StateVetted { 37 vetted = append(vetted, token) 38 } 39 } 40 41 log.Infof("%v vetted records found", len(vetted)) 42 43 // 1. Rebuild the vote summaries cache. 44 // 45 // Records that have finished voting will have a vote summary saved 46 // to the cache. The summary() function builds the vote summary from 47 // scratch for a record and saves it to the cache when appropriate. 48 // The only thing we need to do to rebuild the vote summaries cache 49 // is to delete existing entries and then invoke the summary() 50 // function on all vetted records. 51 52 log.Infof("Building the vote summaries cache") 53 54 bestBlock, err := p.bestBlock() 55 if err != nil { 56 return err 57 } 58 59 summaries := make(map[string]*ticketvote.SummaryReply, len(vetted)) 60 for i, tokenB := range vetted { 61 // Building a vote summary requires retrieving various pieces 62 // of data from the database. This is expensive. Log progress 63 // every 5 records. 64 if i%5 == 0 { 65 log.Infof("Vote summaries cache progress %v/%v", i, len(vetted)) 66 } 67 token := tokenEncode(tokenB) 68 err = p.summaries.Del(token) 69 if err != nil { 70 return err 71 } 72 s, err := p.summary(tokenB, bestBlock) 73 if err != nil { 74 return err 75 } 76 summaries[token] = s 77 } 78 79 log.Infof("Vote summaries cache complete") 80 81 // 2. Rebuild the vote inventory cache. 82 // 83 // All vetted records are included in the vote inventory cache. 84 // This cache is built from scratch. 85 86 log.Infof("Building the vote inventory cache") 87 88 entries := make([]invEntry, 0, len(summaries)) 89 for token, v := range summaries { 90 e := newInvEntry(token, v.Status, v.Timestamp, v.EndBlockHeight) 91 entries = append(entries, *e) 92 } 93 p.inv.Rebuild(entries) 94 95 log.Infof("Vote inventory cache complete") 96 97 // 3. Rebuild the runoff vote submissions cache. 98 // 99 // The submissions cache contains the list of runoff vote parent 100 // records and all of their runoff vote submissions. This cache 101 // is built from scratch. 102 log.Infof("Building the runoff vote submissions cache") 103 104 err = p.rebuildSubsCache(vetted) 105 if err != nil { 106 return err 107 } 108 109 log.Info("Runoff vote submissions cache complete") 110 111 return nil 112 } 113 114 // rebuildSubsCache rebuilds the runoff vote submissions cache from scratch. 115 // 116 // The provided list of tokens should include all runoff vote parent records 117 // as well as all runoff vote submissions. The cache is not rebuilt for any 118 // parent records that are not included in the list. 119 func (p *ticketVotePlugin) rebuildSubsCache(tokens [][]byte) error { 120 // Compile the vote metadata of all runoff vote parents and 121 // submissions. 122 voteMD := make(map[string]*ticketvote.VoteMetadata, len(tokens)) 123 for _, tokenB := range tokens { 124 r, err := p.recordAbridged(tokenB) 125 if err != nil { 126 return err 127 } 128 vmd, err := voteMetadataDecode(r.Files) 129 if err != nil { 130 return err 131 } 132 if !isRunoffParent(vmd) && !isRunoffSub(vmd) { 133 // Not a runoff vote parent or submission 134 continue 135 } 136 if r.RecordMetadata.Status != backend.StatusPublic { 137 // Only public records are included in 138 // the runoff vote submissions cache. 139 continue 140 } 141 142 voteMD[tokenEncode(tokenB)] = vmd 143 } 144 145 // Delete all existing cache entries 146 for token, v := range voteMD { 147 if !isRunoffParent(v) { 148 continue 149 } 150 err := p.subs.DelEntry(token) 151 if err != nil { 152 return err 153 } 154 } 155 156 // Build the cache from scratch 157 for token, v := range voteMD { 158 if !isRunoffSub(v) { 159 continue 160 } 161 p.subs.Add(v.LinkTo, token) 162 } 163 164 return nil 165 }