Konstantin Lebedev
3 years ago
5 changed files with 521 additions and 0 deletions
-
30docker/compose/local-ydb-compose.yml
-
27weed/filer/ydb/readme.md
-
256weed/filer/ydb/ydb_store.go
-
14weed/filer/ydb/ydb_types.go
-
194weed/filer/ydb/ydb_types_ydbgen.go
@ -0,0 +1,30 @@ |
|||
version: '2' |
|||
|
|||
services: |
|||
ydb: |
|||
image: cr.yandex/yc/yandex-docker-local-ydb |
|||
ports: |
|||
- 2135:2135 |
|||
- 8765:8765 |
|||
- 2136:2136 |
|||
volumes: |
|||
- ./seaweedfs.sql:/docker-entrypoint-initdb.d/seaweedfs.sql |
|||
environment: |
|||
- YDB_DEFAULT_LOG_LEVEL=NOTICE |
|||
- GRPC_TLS_PORT=2135 |
|||
- GRPC_PORT=2136 |
|||
- MON_PORT=8765 |
|||
server: |
|||
image: chrislusf/seaweedfs:local |
|||
ports: |
|||
- 9333:9333 |
|||
- 19333:19333 |
|||
- 8084:8080 |
|||
- 18084:18080 |
|||
- 8888:8888 |
|||
- 18888:18888 |
|||
command: "server -ip=server -filer -volume.max=0 -master.volumeSizeLimitMB=1024 -volume.preStopSeconds=1" |
|||
volumes: |
|||
- ./master-cloud.toml:/etc/seaweedfs/master.toml |
|||
depends_on: |
|||
- ydb |
@ -0,0 +1,27 @@ |
|||
## YDB |
|||
|
|||
database: https://github.com/ydb-platform/ydb |
|||
go driver: https://github.com/ydb-platform/ydb-go-sdk |
|||
|
|||
options: |
|||
|
|||
``` |
|||
[ydb] |
|||
enabled=true |
|||
db_name="seaweedfs" |
|||
servers=["http://localhost:8529"] |
|||
#basic auth |
|||
user="root" |
|||
pass="test" |
|||
|
|||
# tls settings |
|||
insecure_skip_verify=true |
|||
``` |
|||
|
|||
get ydb types |
|||
``` |
|||
ydbgen -dir weed/filer/ydb |
|||
``` |
|||
|
|||
i test using this dev database: |
|||
`make dev_ydb` |
@ -0,0 +1,256 @@ |
|||
package ydb |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"github.com/chrislusf/seaweedfs/weed/filer" |
|||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb" |
|||
"github.com/chrislusf/seaweedfs/weed/util" |
|||
"github.com/yandex-cloud/ydb-go-sdk/v2" |
|||
"github.com/yandex-cloud/ydb-go-sdk/v2/connect" |
|||
"github.com/yandex-cloud/ydb-go-sdk/v2/table" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
var ( |
|||
roTX = table.TxControl( |
|||
table.BeginTx(table.WithOnlineReadOnly()), |
|||
table.CommitTx(), |
|||
) |
|||
rwTX = table.TxControl( |
|||
table.BeginTx(table.WithSerializableReadWrite()), |
|||
table.CommitTx(), |
|||
) |
|||
) |
|||
|
|||
const ( |
|||
createQuery = ` |
|||
PRAGMA TablePathPrefix("%s"); |
|||
CREATE TABLE file_meta ( |
|||
dir_hash int64, |
|||
name Utf8, |
|||
directory Utf8, |
|||
meta String, |
|||
PRIMARY KEY (dir_hash, name) |
|||
);` |
|||
insertQuery = ` |
|||
DECLARE $dir_hash int64; |
|||
DECLARE $name AS Utf8; |
|||
DECLARE $directory AS Utf8; |
|||
DECLARE $meta AS String; |
|||
|
|||
UPSERT INTO file_meta |
|||
(dir_hash, name, directory, meta) |
|||
VALUES |
|||
($dir_hash, $name, $directory, $meta);` |
|||
updateQuery = ` |
|||
DECLARE $dir_hash int64; |
|||
DECLARE $name AS Utf8; |
|||
DECLARE $directory AS Utf8; |
|||
DECLARE $meta AS String; |
|||
|
|||
REPLACE INTO file_meta |
|||
(dir_hash, name, directory, meta) |
|||
VALUES |
|||
($dir_hash, $name, $directory, $meta) |
|||
COMMIT;` |
|||
deleteQuery = ` |
|||
DECLARE $dir_hash int64; |
|||
DECLARE $name AS Utf8; |
|||
|
|||
DELETE FROM file_meta |
|||
WHERE dir_hash == $dir_hash AND name == $name; |
|||
COMMIT;` |
|||
findQuery = ` |
|||
DECLARE $dir_hash int64; |
|||
DECLARE $name AS Utf8; |
|||
|
|||
SELECT meta |
|||
FROM file_meta |
|||
WHERE dir_hash == $dir_hash AND name == $name;` |
|||
) |
|||
|
|||
type YdbStore struct { |
|||
SupportBucketTable bool |
|||
DB *connect.Connection |
|||
connParams connect.ConnectParams |
|||
connCtx context.Context |
|||
dirBuckets string |
|||
tablePathPrefix string |
|||
} |
|||
|
|||
func init() { |
|||
filer.Stores = append(filer.Stores, &YdbStore{}) |
|||
} |
|||
|
|||
func (store *YdbStore) GetName() string { |
|||
return "ydb" |
|||
} |
|||
|
|||
func (store *YdbStore) Initialize(configuration util.Configuration, prefix string) (err error) { |
|||
return store.initialize(configuration.GetString(prefix + "coonectionUrl")) |
|||
} |
|||
|
|||
func (store *YdbStore) initialize(sqlUrl string) (err error) { |
|||
store.SupportBucketTable = false |
|||
var cancel context.CancelFunc |
|||
store.connCtx, cancel = context.WithTimeout(context.Background(), 10*time.Second) |
|||
defer cancel() |
|||
store.connParams = connect.MustConnectionString(sqlUrl) |
|||
store.DB, err = connect.New(store.connCtx, store.connParams) |
|||
if err != nil { |
|||
store.DB.Close() |
|||
store.DB = nil |
|||
return fmt.Errorf("can not connect to %s error:%v", sqlUrl, err) |
|||
} |
|||
defer store.DB.Close() |
|||
|
|||
if err = store.DB.EnsurePathExists(store.connCtx, store.connParams.Database()); err != nil { |
|||
return fmt.Errorf("connect to %s error:%v", sqlUrl, err) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (store *YdbStore) insertOrUpdateEntry(ctx context.Context, entry *filer.Entry, query string) (err error) { |
|||
dir, name := entry.FullPath.DirAndName() |
|||
meta, err := entry.EncodeAttributesAndChunks() |
|||
if err != nil { |
|||
return fmt.Errorf("encode %s: %s", entry.FullPath, err) |
|||
} |
|||
fileMeta := FileMeta{util.HashStringToLong(dir), name, dir, meta} |
|||
return table.Retry(ctx, store.DB.Table().Pool(), |
|||
table.OperationFunc(func(ctx context.Context, s *table.Session) (err error) { |
|||
stmt, err := s.Prepare(ctx, store.withPragma(store.getPrefix(dir), query)) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
_, _, err = stmt.Execute(ctx, rwTX, fileMeta.QueryParameters()) |
|||
return err |
|||
}), |
|||
) |
|||
} |
|||
|
|||
func (store *YdbStore) InsertEntry(ctx context.Context, entry *filer.Entry) (err error) { |
|||
return store.insertOrUpdateEntry(ctx, entry, insertQuery) |
|||
} |
|||
|
|||
func (store *YdbStore) UpdateEntry(ctx context.Context, entry *filer.Entry) (err error) { |
|||
return store.insertOrUpdateEntry(ctx, entry, updateQuery) |
|||
} |
|||
|
|||
func (store *YdbStore) FindEntry(ctx context.Context, fullpath util.FullPath) (entry *filer.Entry, err error) { |
|||
dir, name := fullpath.DirAndName() |
|||
var res *table.Result |
|||
err = table.Retry(ctx, store.DB.Table().Pool(), |
|||
table.OperationFunc(func(ctx context.Context, s *table.Session) (err error) { |
|||
stmt, err := s.Prepare(ctx, store.withPragma(store.getPrefix(dir), findQuery)) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
_, res, err = stmt.Execute(ctx, rwTX, table.NewQueryParameters( |
|||
table.ValueParam("$dir_hash", ydb.Int64Value(util.HashStringToLong(dir))), |
|||
table.ValueParam("$name", ydb.UTF8Value(name)))) |
|||
return err |
|||
}), |
|||
) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
for res.NextSet() { |
|||
for res.NextRow() { |
|||
res.SeekItem("meta") |
|||
entry.FullPath = fullpath |
|||
if err := entry.DecodeAttributesAndChunks(util.MaybeDecompressData(res.String())); err != nil { |
|||
return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err) |
|||
} |
|||
return entry, nil |
|||
} |
|||
} |
|||
|
|||
return nil, filer_pb.ErrNotFound |
|||
} |
|||
|
|||
func (store *YdbStore) DeleteEntry(ctx context.Context, fullpath util.FullPath) (err error) { |
|||
dir, name := fullpath.DirAndName() |
|||
return table.Retry(ctx, store.DB.Table().Pool(), |
|||
table.OperationFunc(func(ctx context.Context, s *table.Session) (err error) { |
|||
stmt, err := s.Prepare(ctx, store.withPragma(store.getPrefix(dir), deleteQuery)) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
_, _, err = stmt.Execute(ctx, rwTX, table.NewQueryParameters( |
|||
table.ValueParam("$dir_hash", ydb.Int64Value(util.HashStringToLong(dir))), |
|||
table.ValueParam("$name", ydb.UTF8Value(name)))) |
|||
return err |
|||
}), |
|||
) |
|||
} |
|||
|
|||
func (store *YdbStore) DeleteFolderChildren(ctx context.Context, fullpath util.FullPath) (err error) { |
|||
return nil |
|||
} |
|||
|
|||
func (store *YdbStore) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) { |
|||
//TODO implement me
|
|||
panic("implement me") |
|||
} |
|||
|
|||
func (store *YdbStore) ListDirectoryPrefixedEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) { |
|||
//TODO implement me
|
|||
panic("implement me") |
|||
} |
|||
|
|||
func (store *YdbStore) BeginTransaction(ctx context.Context) (context.Context, error) { |
|||
//TODO implement me
|
|||
panic("implement me") |
|||
} |
|||
|
|||
func (store *YdbStore) CommitTransaction(ctx context.Context) error { |
|||
//TODO implement me
|
|||
panic("implement me") |
|||
} |
|||
|
|||
func (store *YdbStore) RollbackTransaction(ctx context.Context) error { |
|||
//TODO implement me
|
|||
panic("implement me") |
|||
} |
|||
|
|||
func (store *YdbStore) KvPut(ctx context.Context, key []byte, value []byte) (err error) { |
|||
//TODO implement me
|
|||
panic("implement me") |
|||
} |
|||
|
|||
func (store *YdbStore) KvGet(ctx context.Context, key []byte) (value []byte, err error) { |
|||
//TODO implement me
|
|||
panic("implement me") |
|||
} |
|||
|
|||
func (store *YdbStore) KvDelete(ctx context.Context, key []byte) (err error) { |
|||
//TODO implement me
|
|||
panic("implement me") |
|||
} |
|||
|
|||
func (store *YdbStore) Shutdown() { |
|||
//TODO implement me
|
|||
panic("implement me") |
|||
} |
|||
|
|||
func (store *YdbStore) getPrefix(dir string) string { |
|||
prefixBuckets := store.dirBuckets + "/" |
|||
if strings.HasPrefix(dir, prefixBuckets) { |
|||
// detect bucket
|
|||
bucketAndDir := dir[len(prefixBuckets):] |
|||
if t := strings.Index(bucketAndDir, "/"); t > 0 { |
|||
return bucketAndDir[:t] |
|||
} |
|||
} |
|||
return "" |
|||
} |
|||
|
|||
func (store *YdbStore) withPragma(prefix, query string) string { |
|||
if store.tablePathPrefix != "" && store.tablePathPrefix != "/" { |
|||
prefix = store.tablePathPrefix + "/" + prefix |
|||
} |
|||
return `PRAGMA TablePathPrefix("` + prefix + `");` + query |
|||
} |
@ -0,0 +1,14 @@ |
|||
package ydb |
|||
|
|||
//go:generate ydbgen
|
|||
|
|||
//ydb:gen
|
|||
type FileMeta struct { |
|||
DirHash int64 `ydb:"type:int64"` |
|||
Name string `ydb:"type:utf8"` |
|||
Directory string `ydb:"type:utf8"` |
|||
Meta []byte `ydb:"-"` |
|||
} |
|||
|
|||
//ydb:gen scan,value
|
|||
type FileMetas []FileMeta |
@ -0,0 +1,194 @@ |
|||
// Code generated by ydbgen; DO NOT EDIT.
|
|||
|
|||
package ydb |
|||
|
|||
import ( |
|||
"strconv" |
|||
|
|||
"github.com/yandex-cloud/ydb-go-sdk/v2" |
|||
"github.com/yandex-cloud/ydb-go-sdk/v2/table" |
|||
) |
|||
|
|||
var ( |
|||
_ = strconv.Itoa |
|||
_ = ydb.StringValue |
|||
_ = table.NewQueryParameters |
|||
) |
|||
|
|||
func (f *FileMeta) Scan(res *table.Result) (err error) { |
|||
res.SeekItem("dir_hash") |
|||
f.DirHash = res.Int64() |
|||
|
|||
res.SeekItem("name") |
|||
f.Name = res.UTF8() |
|||
|
|||
res.SeekItem("directory") |
|||
f.Directory = res.UTF8() |
|||
|
|||
return res.Err() |
|||
} |
|||
|
|||
func (f *FileMeta) QueryParameters() *table.QueryParameters { |
|||
var v0 ydb.Value |
|||
{ |
|||
vp0 := ydb.Int64Value(f.DirHash) |
|||
v0 = vp0 |
|||
} |
|||
var v1 ydb.Value |
|||
{ |
|||
vp0 := ydb.UTF8Value(f.Name) |
|||
v1 = vp0 |
|||
} |
|||
var v2 ydb.Value |
|||
{ |
|||
vp0 := ydb.UTF8Value(f.Directory) |
|||
v2 = vp0 |
|||
} |
|||
return table.NewQueryParameters( |
|||
table.ValueParam("$dir_hash", v0), |
|||
table.ValueParam("$name", v1), |
|||
table.ValueParam("$directory", v2), |
|||
) |
|||
} |
|||
|
|||
func (f *FileMeta) StructValue() ydb.Value { |
|||
var v0 ydb.Value |
|||
{ |
|||
var v1 ydb.Value |
|||
{ |
|||
vp0 := ydb.Int64Value(f.DirHash) |
|||
v1 = vp0 |
|||
} |
|||
var v2 ydb.Value |
|||
{ |
|||
vp0 := ydb.UTF8Value(f.Name) |
|||
v2 = vp0 |
|||
} |
|||
var v3 ydb.Value |
|||
{ |
|||
vp0 := ydb.UTF8Value(f.Directory) |
|||
v3 = vp0 |
|||
} |
|||
v0 = ydb.StructValue( |
|||
ydb.StructFieldValue("dir_hash", v1), |
|||
ydb.StructFieldValue("name", v2), |
|||
ydb.StructFieldValue("directory", v3), |
|||
) |
|||
} |
|||
return v0 |
|||
} |
|||
|
|||
func (f *FileMeta) StructType() ydb.Type { |
|||
var t0 ydb.Type |
|||
{ |
|||
fs0 := make([]ydb.StructOption, 3) |
|||
var t1 ydb.Type |
|||
{ |
|||
tp0 := ydb.TypeInt64 |
|||
t1 = tp0 |
|||
} |
|||
fs0[0] = ydb.StructField("dir_hash", t1) |
|||
var t2 ydb.Type |
|||
{ |
|||
tp0 := ydb.TypeUTF8 |
|||
t2 = tp0 |
|||
} |
|||
fs0[1] = ydb.StructField("name", t2) |
|||
var t3 ydb.Type |
|||
{ |
|||
tp0 := ydb.TypeUTF8 |
|||
t3 = tp0 |
|||
} |
|||
fs0[2] = ydb.StructField("directory", t3) |
|||
t0 = ydb.Struct(fs0...) |
|||
} |
|||
return t0 |
|||
} |
|||
|
|||
func (fs *FileMetas) Scan(res *table.Result) (err error) { |
|||
for res.NextRow() { |
|||
var x0 FileMeta |
|||
res.SeekItem("dir_hash") |
|||
x0.DirHash = res.Int64() |
|||
|
|||
res.SeekItem("name") |
|||
x0.Name = res.UTF8() |
|||
|
|||
res.SeekItem("directory") |
|||
x0.Directory = res.UTF8() |
|||
|
|||
if res.Err() == nil { |
|||
*fs = append(*fs, x0) |
|||
} |
|||
} |
|||
return res.Err() |
|||
} |
|||
|
|||
func (fs FileMetas) ListValue() ydb.Value { |
|||
var list0 ydb.Value |
|||
vs0 := make([]ydb.Value, len(fs)) |
|||
for i0, item0 := range fs { |
|||
var v0 ydb.Value |
|||
{ |
|||
var v1 ydb.Value |
|||
{ |
|||
var v2 ydb.Value |
|||
{ |
|||
vp0 := ydb.Int64Value(item0.DirHash) |
|||
v2 = vp0 |
|||
} |
|||
var v3 ydb.Value |
|||
{ |
|||
vp0 := ydb.UTF8Value(item0.Name) |
|||
v3 = vp0 |
|||
} |
|||
var v4 ydb.Value |
|||
{ |
|||
vp0 := ydb.UTF8Value(item0.Directory) |
|||
v4 = vp0 |
|||
} |
|||
v1 = ydb.StructValue( |
|||
ydb.StructFieldValue("dir_hash", v2), |
|||
ydb.StructFieldValue("name", v3), |
|||
ydb.StructFieldValue("directory", v4), |
|||
) |
|||
} |
|||
v0 = v1 |
|||
} |
|||
vs0[i0] = v0 |
|||
} |
|||
if len(vs0) == 0 { |
|||
var t1 ydb.Type |
|||
{ |
|||
var t2 ydb.Type |
|||
{ |
|||
fs0 := make([]ydb.StructOption, 3) |
|||
var t3 ydb.Type |
|||
{ |
|||
tp0 := ydb.TypeInt64 |
|||
t3 = tp0 |
|||
} |
|||
fs0[0] = ydb.StructField("dir_hash", t3) |
|||
var t4 ydb.Type |
|||
{ |
|||
tp0 := ydb.TypeUTF8 |
|||
t4 = tp0 |
|||
} |
|||
fs0[1] = ydb.StructField("name", t4) |
|||
var t5 ydb.Type |
|||
{ |
|||
tp0 := ydb.TypeUTF8 |
|||
t5 = tp0 |
|||
} |
|||
fs0[2] = ydb.StructField("directory", t5) |
|||
t2 = ydb.Struct(fs0...) |
|||
} |
|||
t1 = t2 |
|||
} |
|||
t0 := ydb.List(t1) |
|||
list0 = ydb.ZeroValue(t0) |
|||
} else { |
|||
list0 = ydb.ListValue(vs0...) |
|||
} |
|||
return list0 |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue