You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bunt_datastore.go 2.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // Copyright (c) 2022 Shivaram Lingamneni
  2. // released under the MIT license
  3. package bunt
  4. import (
  5. "fmt"
  6. "strings"
  7. "time"
  8. "github.com/tidwall/buntdb"
  9. "github.com/ergochat/ergo/irc/datastore"
  10. "github.com/ergochat/ergo/irc/logger"
  11. "github.com/ergochat/ergo/irc/utils"
  12. )
  13. // BuntKey yields a string key corresponding to a (table, UUID) pair.
  14. // Ideally this would not be public, but some of the migration code
  15. // needs it.
  16. func BuntKey(table datastore.Table, uuid utils.UUID) string {
  17. return fmt.Sprintf("%x %s", table, uuid.String())
  18. }
  19. // buntdbDatastore implements datastore.Datastore using a buntdb.
  20. type buntdbDatastore struct {
  21. db *buntdb.DB
  22. logger *logger.Manager
  23. }
  24. // NewBuntdbDatastore returns a datastore.Datastore backed by buntdb.
  25. func NewBuntdbDatastore(db *buntdb.DB, logger *logger.Manager) datastore.Datastore {
  26. return &buntdbDatastore{
  27. db: db,
  28. logger: logger,
  29. }
  30. }
  31. func (b *buntdbDatastore) Backoff() time.Duration {
  32. return 0
  33. }
  34. func (b *buntdbDatastore) GetAll(table datastore.Table) (result []datastore.KV, err error) {
  35. tablePrefix := fmt.Sprintf("%x ", table)
  36. err = b.db.View(func(tx *buntdb.Tx) error {
  37. err := tx.AscendGreaterOrEqual("", tablePrefix, func(key, value string) bool {
  38. if !strings.HasPrefix(key, tablePrefix) {
  39. return false
  40. }
  41. uuid, err := utils.DecodeUUID(strings.TrimPrefix(key, tablePrefix))
  42. if err == nil {
  43. result = append(result, datastore.KV{UUID: uuid, Value: []byte(value)})
  44. } else {
  45. b.logger.Error("datastore", "invalid uuid", key)
  46. }
  47. return true
  48. })
  49. return err
  50. })
  51. return
  52. }
  53. func (b *buntdbDatastore) Get(table datastore.Table, uuid utils.UUID) (value []byte, err error) {
  54. buntKey := BuntKey(table, uuid)
  55. var result string
  56. err = b.db.View(func(tx *buntdb.Tx) error {
  57. result, err = tx.Get(buntKey)
  58. return err
  59. })
  60. return []byte(result), err
  61. }
  62. func (b *buntdbDatastore) Set(table datastore.Table, uuid utils.UUID, value []byte, expiration time.Time) (err error) {
  63. buntKey := BuntKey(table, uuid)
  64. var setOptions *buntdb.SetOptions
  65. if !expiration.IsZero() {
  66. ttl := time.Until(expiration)
  67. if ttl > 0 {
  68. setOptions = &buntdb.SetOptions{Expires: true, TTL: ttl}
  69. } else {
  70. return nil // it already expired, i guess?
  71. }
  72. }
  73. strVal := string(value)
  74. err = b.db.Update(func(tx *buntdb.Tx) error {
  75. _, _, err := tx.Set(buntKey, strVal, setOptions)
  76. return err
  77. })
  78. return
  79. }
  80. func (b *buntdbDatastore) Delete(table datastore.Table, key utils.UUID) (err error) {
  81. buntKey := BuntKey(table, key)
  82. err = b.db.Update(func(tx *buntdb.Tx) error {
  83. _, err := tx.Delete(buntKey)
  84. return err
  85. })
  86. // deleting a nonexistent key is not considered an error
  87. switch err {
  88. case buntdb.ErrNotFound:
  89. return nil
  90. default:
  91. return err
  92. }
  93. }