feat: Initial

This commit is contained in:
Artёm Mishin committed 2025-04-25 12:51:59 +03:00
commit ed9af2b459
10 files changed
+376

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/universe_badger.iml" filepath="$PROJECT_DIR$/.idea/universe_badger.iml" />
</modules>
</component>
</project>
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
Generated
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" />
</project>
+31
View File
@@ -0,0 +1,31 @@
module go.mrlokop.dev/universe/data/badger
go 1.24
require (
github.com/dgraph-io/badger/v4 v4.7.0
go.uber.org/fx v1.23.0
go.uber.org/zap v1.27.0
universe.mrlokop.dev/core v0.0.0-20250425091205-db1d0545d9c6
universe.mrlokop.dev/key_value v0.0.0-20250425093329-a79c041fe7e1
)
require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.uber.org/dig v1.18.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.32.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
)
+61
View File
@@ -0,0 +1,61 @@
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y=
github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA=
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.uber.org/dig v1.18.1 h1:rLww6NuajVjeQn+49u5NcezUJEGwd5uXmyoCKW2g5Es=
go.uber.org/dig v1.18.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg=
go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
universe.mrlokop.dev/core v0.0.0-20250425091205-db1d0545d9c6 h1:aM0o2QFSizLY9KOXuoOmsp/o345yJEptgq8egA85VnM=
universe.mrlokop.dev/core v0.0.0-20250425091205-db1d0545d9c6/go.mod h1:1xEErU5FKve38at9zd2RZDDbojuebd9rGpWzeUD4zu4=
universe.mrlokop.dev/key_value v0.0.0-20250425091341-1d08501b51d7 h1:jJmVvb+UVioVyJpYe5jF3W0iCxNF+RR4uR4h695aYVI=
universe.mrlokop.dev/key_value v0.0.0-20250425091341-1d08501b51d7/go.mod h1:ajRUrvqBdKaLg9vINxxCEYme+DXfzotA+HIfje+mmZM=
universe.mrlokop.dev/key_value v0.0.0-20250425093329-a79c041fe7e1 h1:dpEjqVgueSxw+G73g9g80B0yhJKIu4zWwO38ovbRoiE=
universe.mrlokop.dev/key_value v0.0.0-20250425093329-a79c041fe7e1/go.mod h1:WNqaFQruy5ich5wzhivNQf6PljJ9QxiBJ8gmvJMemG4=
+32
View File
@@ -0,0 +1,32 @@
package universe_badger
import (
"github.com/dgraph-io/badger/v4"
kv "universe.mrlokop.dev/key_value"
)
type keyValueProvider struct {
db *badger.DB
}
func (k *keyValueProvider) HasCapability(capability kv.Capability) bool {
switch capability {
default:
return false
}
}
func (k *keyValueProvider) ProvideBinary(prefix string) (kv.KeyValueStoreBinary, error) {
return &keyValueStorage{
db: k.db,
prefix: prefix,
}, nil
}
func (k *keyValueProvider) ProvideString(prefix string) (kv.KeyValueStoreString, error) {
bin, err := k.ProvideBinary(prefix)
if err != nil {
return nil, err
}
return kv.WrapStringToBinary(bin), err
}
+138
View File
@@ -0,0 +1,138 @@
package universe_badger
import (
"context"
"github.com/dgraph-io/badger/v4"
"time"
"universe.mrlokop.dev/core/errors"
kv "universe.mrlokop.dev/key_value"
)
func wrapErr(err error) error {
if errors.Is(err, badger.ErrKeyNotFound) {
return kv.ErrNoSuchKey.CreateWithCause(err)
}
return err
}
type keyValueStorage struct {
db *badger.DB
prefix string
}
func (k *keyValueStorage) Get(ctx context.Context, key string) ([]byte, error) {
var result []byte
err := k.db.View(func(txn *badger.Txn) error {
keyBytes := []byte(k.prefix + key)
item, err := txn.Get(keyBytes)
if err != nil {
return wrapErr(err)
}
result = make([]byte, item.ValueSize())
_, err = item.ValueCopy(result)
return wrapErr(err)
})
return result, err
}
func (k *keyValueStorage) Has(ctx context.Context, key string) (bool, error) {
var exists bool
err := k.db.View(func(txn *badger.Txn) error {
keyBytes := []byte(k.prefix + key)
_, err := txn.Get(keyBytes)
if err != nil {
if errors.Is(err, badger.ErrKeyNotFound) {
exists = false
return nil
}
return wrapErr(err)
}
exists = true
return nil
})
return exists, err
}
func (k *keyValueStorage) Set(ctx context.Context, key string, value []byte) error {
return k.db.Update(func(txn *badger.Txn) error {
keyBytes := []byte(k.prefix + key)
return wrapErr(txn.Set(keyBytes, value))
})
}
func (k *keyValueStorage) SetWithExpiration(ctx context.Context, key string, value []byte, expiresAt time.Time) error {
return k.db.Update(func(txn *badger.Txn) error {
keyBytes := []byte(k.prefix + key)
entry := badger.NewEntry(keyBytes, value)
entry.ExpiresAt = uint64(expiresAt.Unix())
return wrapErr(txn.SetEntry(entry))
})
}
func (k *keyValueStorage) SetIfNotExist(ctx context.Context, key string, value []byte) (bool, error) {
var wasSet bool
err := k.db.Update(func(txn *badger.Txn) error {
keyBytes := []byte(k.prefix + key)
_, err := txn.Get(keyBytes)
if err != nil {
if errors.Is(err, badger.ErrKeyNotFound) {
wasSet = true
return wrapErr(txn.Set(keyBytes, value))
}
}
return wrapErr(err)
})
return wasSet, wrapErr(err)
}
func (k *keyValueStorage) SetIfNotExistWithExpiration(ctx context.Context, key string, value []byte, expiration time.Time) (bool, error) {
var wasSet bool
err := k.db.Update(func(txn *badger.Txn) error {
keyBytes := []byte(k.prefix + key)
_, err := txn.Get(keyBytes)
if err != nil {
if errors.Is(err, badger.ErrKeyNotFound) {
wasSet = true
entry := badger.NewEntry(keyBytes, value)
entry.ExpiresAt = uint64(expiration.Unix())
return wrapErr(txn.SetEntry(entry))
}
}
return wrapErr(err)
})
return wasSet, err
}
func (k *keyValueStorage) ExpireAt(ctx context.Context, key string, expiration time.Time) error {
return k.db.Update(func(txn *badger.Txn) error {
keyBytes := []byte(k.prefix + key)
item, err := txn.Get(keyBytes)
if err != nil {
return wrapErr(err)
}
copyOfBytes := make([]byte, item.ValueSize())
_, err = item.ValueCopy(copyOfBytes)
if err != nil {
return wrapErr(err)
}
entry := badger.NewEntry(keyBytes, copyOfBytes)
entry.ExpiresAt = uint64(expiration.Unix())
return wrapErr(txn.SetEntry(entry))
})
}
func (k *keyValueStorage) Remove(ctx context.Context, key string) error {
return k.db.Update(func(txn *badger.Txn) error {
keyBytes := []byte(k.prefix + key)
return wrapErr(txn.Delete(keyBytes))
})
}
func (k *keyValueStorage) HasCapability(capability kv.Capability) bool {
switch capability {
default:
return false
}
}
+31
View File
@@ -0,0 +1,31 @@
package universe_badger
import (
"fmt"
"go.uber.org/zap"
"strings"
)
type zapRedirectingLogger struct {
target *zap.Logger
}
func (z *zapRedirectingLogger) Errorf(s string, i ...interface{}) {
s, _ = strings.CutSuffix(s, "\n")
z.target.Error(fmt.Sprintf(s, i...))
}
func (z *zapRedirectingLogger) Warningf(s string, i ...interface{}) {
s, _ = strings.CutSuffix(s, "\n")
z.target.Warn(fmt.Sprintf(s, i...))
}
func (z *zapRedirectingLogger) Infof(s string, i ...interface{}) {
s, _ = strings.CutSuffix(s, "\n")
z.target.Info(fmt.Sprintf(s, i...))
}
func (z *zapRedirectingLogger) Debugf(s string, i ...interface{}) {
s, _ = strings.CutSuffix(s, "\n")
z.target.Debug(fmt.Sprintf(s, i...))
}
+54
View File
@@ -0,0 +1,54 @@
package universe_badger
import (
"github.com/dgraph-io/badger/v4"
"go.uber.org/fx"
"go.uber.org/zap"
um "universe.mrlokop.dev/core/modules"
kv "universe.mrlokop.dev/key_value"
)
type moduleConfig struct {
Path string
}
var (
Module = um.SimpleModule("universe:badger", func(ctx um.ModuleFactoryContext) error {
cfg := &moduleConfig{}
err := ctx.ExtractConfig(cfg)
if err != nil {
return err
}
ctx.Provide(fx.Annotate(func(
logger *zap.Logger,
) (*badger.DB, error) {
var (
ll = ctx.ConfigureLogger(logger)
opts = badger.DefaultOptions(cfg.Path)
)
if cfg.Path == "" {
opts.WithInMemory(true)
}
opts.WithLogger(&zapRedirectingLogger{target: ll})
db, err := badger.Open(opts)
if err != nil {
return nil, err
}
return db, nil
}, fx.ResultTags(ctx.NameTag())))
ctx.Provide(fx.Annotate(func(
badger *badger.DB,
logger *zap.Logger,
) kv.KeyValueProvider {
return &keyValueProvider{}
}, fx.ParamTags(ctx.NameTag()), fx.ResultTags(ctx.NameTag())))
return nil
})
)