github.com/decred/politeia@v1.4.0/politeiad/backendv2/tstorebe/plugins/ticketvote/summaries.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  	"encoding/json"
     9  	"errors"
    10  	"strings"
    11  
    12  	"github.com/decred/politeia/politeiad/backendv2/tstorebe/plugins"
    13  	"github.com/decred/politeia/politeiad/plugins/ticketvote"
    14  	"github.com/decred/politeia/util"
    15  )
    16  
    17  var (
    18  	// errSummaryNotFound is returned when a vote summary is not found in the
    19  	// cache for a record.
    20  	errSummaryNotFound = errors.New("summary not found")
    21  )
    22  
    23  // summariesClient provides an API for interacting with the vote summaries
    24  // cache. The data is saved to the TstoreClient provided plugin cache.
    25  //
    26  // Vote summaries are only cached once the vote has finished and additional
    27  // updates to the record's vote data are no longer possible.
    28  //
    29  // tstore does not provide plugins with a sql transaction that can be used
    30  // to execute multiple database requests atomically during cache updates.
    31  // Concurrent access must be controlled locally using a mutex, but since the
    32  // summaries are only written once and never updated, we don't need to worry
    33  // about concurrency issues.
    34  type summariesClient struct {
    35  	tstore plugins.TstoreClient
    36  }
    37  
    38  // newSummariesClient returns a new summariesClient.
    39  func newSummariesClient(tstore plugins.TstoreClient) *summariesClient {
    40  	return &summariesClient{
    41  		tstore: tstore,
    42  	}
    43  }
    44  
    45  // Save saves a vote summary to the cache.
    46  func (c *summariesClient) Save(token string, s ticketvote.SummaryReply) error {
    47  	key, err := buildSummaryKey(token)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	b, err := json.Marshal(s)
    52  	if err != nil {
    53  		return err
    54  	}
    55  	err = c.tstore.CachePut(map[string][]byte{key: b}, false)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	log.Debugf("Vote summary saved for %v", token)
    61  
    62  	return nil
    63  }
    64  
    65  // Del deletes a vote summary from the cache.
    66  //
    67  // An error is not returned if an entry is not found in the cache for the
    68  // provided token.
    69  func (c *summariesClient) Del(token string) error {
    70  	key, err := buildSummaryKey(token)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	return c.tstore.CacheDel([]string{key})
    75  }
    76  
    77  // Get retrieves a vote summary from the cache.
    78  //
    79  // An errSummaryNotFound is returned if a vote summary is not found in the
    80  // cache for the record.
    81  func (c *summariesClient) Get(token string) (*ticketvote.SummaryReply, error) {
    82  	key, err := buildSummaryKey(token)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	entries, err := c.tstore.CacheGet([]string{key})
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	b, ok := entries[key]
    91  	if !ok {
    92  		return nil, errSummaryNotFound
    93  	}
    94  	var sr ticketvote.SummaryReply
    95  	err = json.Unmarshal(b, &sr)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	return &sr, nil
   100  }
   101  
   102  const (
   103  	// summaryKey is the key-value store key for an entry in the vote summaries
   104  	// cache. Each vote summary entry is saved as an individual database entry.
   105  	// The "{shorttoken}" is replaced with the record's short token.
   106  	summaryKey = "summary-{shorttoken}"
   107  )
   108  
   109  // buildSummaryKey returns the key-value store key for an entry in the vote
   110  // summaries cache.
   111  func buildSummaryKey(token string) (string, error) {
   112  	s, err := util.ShortTokenString(token)
   113  	if err != nil {
   114  		return "", err
   115  	}
   116  	return strings.Replace(summaryKey, "{shorttoken}", s, 1), nil
   117  }