github.com/dschalla/mattermost-server@v4.8.1-rc1+incompatible/store/redis_supplier.go (about)

     1  // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package store
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/gob"
    10  
    11  	"time"
    12  
    13  	l4g "github.com/alecthomas/log4go"
    14  	"github.com/go-redis/redis"
    15  	"github.com/mattermost/mattermost-server/model"
    16  )
    17  
    18  const REDIS_EXPIRY_TIME = 30 * time.Minute
    19  
    20  type RedisSupplier struct {
    21  	next   LayeredStoreSupplier
    22  	client *redis.Client
    23  }
    24  
    25  func GetBytes(key interface{}) ([]byte, error) {
    26  	var buf bytes.Buffer
    27  	enc := gob.NewEncoder(&buf)
    28  	err := enc.Encode(key)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	return buf.Bytes(), nil
    33  }
    34  
    35  func DecodeBytes(input []byte, thing interface{}) error {
    36  	dec := gob.NewDecoder(bytes.NewReader(input))
    37  	return dec.Decode(thing)
    38  }
    39  
    40  func NewRedisSupplier() *RedisSupplier {
    41  	supplier := &RedisSupplier{}
    42  
    43  	supplier.client = redis.NewClient(&redis.Options{
    44  		Addr:     "localhost:6379",
    45  		Password: "",
    46  		DB:       0,
    47  	})
    48  
    49  	if _, err := supplier.client.Ping().Result(); err != nil {
    50  		l4g.Error("Unable to ping redis server: " + err.Error())
    51  		return nil
    52  	}
    53  
    54  	return supplier
    55  }
    56  
    57  func (s *RedisSupplier) save(key string, value interface{}, expiry time.Duration) error {
    58  	if bytes, err := GetBytes(value); err != nil {
    59  		return err
    60  	} else {
    61  		if err := s.client.Set(key, bytes, expiry).Err(); err != nil {
    62  			return err
    63  		}
    64  	}
    65  	return nil
    66  }
    67  
    68  func (s *RedisSupplier) load(key string, writeTo interface{}) (bool, error) {
    69  	if data, err := s.client.Get(key).Bytes(); err != nil {
    70  		if err == redis.Nil {
    71  			return false, nil
    72  		} else {
    73  			return false, err
    74  		}
    75  	} else {
    76  		if err := DecodeBytes(data, writeTo); err != nil {
    77  			return false, err
    78  		}
    79  	}
    80  	return true, nil
    81  }
    82  
    83  func (s *RedisSupplier) SetChainNext(next LayeredStoreSupplier) {
    84  	s.next = next
    85  }
    86  
    87  func (s *RedisSupplier) Next() LayeredStoreSupplier {
    88  	return s.next
    89  }
    90  
    91  func (s *RedisSupplier) ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
    92  	if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
    93  		l4g.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
    94  	}
    95  	return s.Next().ReactionSave(ctx, reaction, hints...)
    96  }
    97  
    98  func (s *RedisSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
    99  	if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
   100  		l4g.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
   101  	}
   102  	return s.Next().ReactionDelete(ctx, reaction, hints...)
   103  }
   104  
   105  func (s *RedisSupplier) ReactionGetForPost(ctx context.Context, postId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
   106  	var resultdata []*model.Reaction
   107  	found, err := s.load("reactions:"+postId, &resultdata)
   108  	if found {
   109  		result := NewSupplierResult()
   110  		result.Data = resultdata
   111  		return result
   112  	}
   113  	if err != nil {
   114  		l4g.Error("Redis encountered an error on read: " + err.Error())
   115  	}
   116  
   117  	result := s.Next().ReactionGetForPost(ctx, postId, hints...)
   118  
   119  	if err := s.save("reactions:"+postId, result.Data, REDIS_EXPIRY_TIME); err != nil {
   120  		l4g.Error("Redis encountered and error on write: " + err.Error())
   121  	}
   122  
   123  	return result
   124  }
   125  
   126  func (s *RedisSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
   127  	// Ignoring this. It's probably OK to have the emoji slowly expire from Redis.
   128  	return s.Next().ReactionDeleteAllWithEmojiName(ctx, emojiName, hints...)
   129  }
   130  
   131  func (s *RedisSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
   132  	// Ignoring this. It's probably OK to have the emoji slowly expire from Redis.
   133  	return s.Next().ReactionPermanentDeleteBatch(ctx, endTime, limit, hints...)
   134  }