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  }