Browse Source

Merge branch 'master' into mq

mq
chrislu 5 days ago
parent
commit
d0e24e8fac
  1. 2
      k8s/charts/seaweedfs/Chart.yaml
  2. 16
      k8s/charts/seaweedfs/templates/filer-statefulset.yaml
  3. 18
      k8s/charts/seaweedfs/templates/master-statefulset.yaml
  4. 9
      k8s/charts/seaweedfs/templates/volume-statefulset.yaml
  5. 71
      weed/command/scaffold/filer.toml
  6. 15
      weed/filer/cassandra/README.txt
  7. 15
      weed/filer/cassandra2/README.txt
  8. 221
      weed/filer/cassandra2/cassandra_store.go
  9. 63
      weed/filer/cassandra2/cassandra_store_kv.go
  10. 5
      weed/filer/redis/README.md
  11. 4
      weed/filer/redis3/README.md

2
k8s/charts/seaweedfs/Chart.yaml

@ -3,4 +3,4 @@ description: SeaweedFS
name: seaweedfs
appVersion: "3.85"
# Dev note: Trigger a helm chart release by `git tag -a helm-<version>`
version: 4.0.385
version: 4.0.386

16
k8s/charts/seaweedfs/templates/filer-statefulset.yaml

@ -248,19 +248,15 @@ spec:
- name: ca-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/ca/
- name: master-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/master/
- name: volume-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/volume/
- name: filer-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/filer/
{{- if .Values.filer.s3.enabled }}
- name: client-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/client/
{{- end }}
{{- end }}
{{ tpl .Values.filer.extraVolumeMounts . | nindent 12 | trim }}
ports:
- containerPort: {{ .Values.filer.port }}
@ -367,19 +363,15 @@ spec:
- name: ca-cert
secret:
secretName: {{ template "seaweedfs.name" . }}-ca-cert
- name: master-cert
secret:
secretName: {{ template "seaweedfs.name" . }}-master-cert
- name: volume-cert
secret:
secretName: {{ template "seaweedfs.name" . }}-volume-cert
- name: filer-cert
secret:
secretName: {{ template "seaweedfs.name" . }}-filer-cert
{{- if .Values.filer.s3.enabled }}
- name: client-cert
secret:
secretName: {{ template "seaweedfs.name" . }}-client-cert
{{- end }}
{{- end }}
{{ tpl .Values.filer.extraVolumes . | indent 8 | trim }}
{{- if .Values.filer.nodeSelector }}
nodeSelector:

18
k8s/charts/seaweedfs/templates/master-statefulset.yaml

@ -191,15 +191,6 @@ spec:
- name: master-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/master/
- name: volume-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/volume/
- name: filer-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/filer/
- name: client-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/client/
{{- end }}
{{ tpl .Values.master.extraVolumeMounts . | nindent 12 | trim }}
ports:
@ -289,15 +280,6 @@ spec:
- name: master-cert
secret:
secretName: {{ template "seaweedfs.name" . }}-master-cert
- name: volume-cert
secret:
secretName: {{ template "seaweedfs.name" . }}-volume-cert
- name: filer-cert
secret:
secretName: {{ template "seaweedfs.name" . }}-filer-cert
- name: client-cert
secret:
secretName: {{ template "seaweedfs.name" . }}-client-cert
{{- end }}
{{ tpl .Values.master.extraVolumes . | indent 8 | trim }}
{{- if .Values.master.nodeSelector }}

9
k8s/charts/seaweedfs/templates/volume-statefulset.yaml

@ -207,18 +207,9 @@ spec:
- name: ca-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/ca/
- name: master-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/master/
- name: volume-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/volume/
- name: filer-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/filer/
- name: client-cert
readOnly: true
mountPath: /usr/local/share/ca-certificates/client/
{{- end }}
{{ tpl .Values.volume.extraVolumeMounts . | nindent 12 | trim }}
ports:

71
weed/command/scaffold/filer.toml

@ -139,12 +139,13 @@ connection_max_lifetime_seconds = 0
enableUpsert = true
upsertQuery = """UPSERT INTO "%[1]s" (dirhash,name,directory,meta) VALUES($1,$2,$3,$4)"""
[cassandra]
[cassandra2]
# CREATE TABLE filemeta (
# dirhash bigint,
# directory varchar,
# name varchar,
# meta blob,
# PRIMARY KEY (directory, name)
# PRIMARY KEY ((dirhash, directory), name)
# ) WITH CLUSTERING ORDER BY (name ASC);
enabled = false
keyspace = "seaweedfs"
@ -199,70 +200,6 @@ routeByLatency = false
# This changes the data layout. Only add new directories. Removing/Updating will cause data loss.
superLargeDirectories = []
[redis_lua]
enabled = false
address = "localhost:6379"
password = ""
database = 0
# This changes the data layout. Only add new directories. Removing/Updating will cause data loss.
superLargeDirectories = []
[redis_lua_sentinel]
enabled = false
addresses = ["172.22.12.7:26379","172.22.12.8:26379","172.22.12.9:26379"]
masterName = "master"
username = ""
password = ""
database = 0
[redis_lua_cluster]
enabled = false
addresses = [
"localhost:30001",
"localhost:30002",
"localhost:30003",
"localhost:30004",
"localhost:30005",
"localhost:30006",
]
password = ""
# allows reads from slave servers or the master, but all writes still go to the master
readOnly = false
# automatically use the closest Redis server for reads
routeByLatency = false
# This changes the data layout. Only add new directories. Removing/Updating will cause data loss.
superLargeDirectories = []
[redis3] # beta
enabled = false
address = "localhost:6379"
password = ""
database = 0
[redis3_sentinel]
enabled = false
addresses = ["172.22.12.7:26379","172.22.12.8:26379","172.22.12.9:26379"]
masterName = "master"
username = ""
password = ""
database = 0
[redis_cluster3] # beta
enabled = false
addresses = [
"localhost:30001",
"localhost:30002",
"localhost:30003",
"localhost:30004",
"localhost:30005",
"localhost:30006",
]
password = ""
# allows reads from slave servers or the master, but all writes still go to the master
readOnly = false
# automatically use the closest Redis server for reads
routeByLatency = false
[etcd]
enabled = false
servers = "localhost:2379"
@ -333,7 +270,7 @@ dialTimeOut = 10
##########################
# To add path-specific filer store:
#
# 1. Add a name following the store type separated by a dot ".". E.g., cassandra.tmp
# 1. Add a name following the store type separated by a dot ".". E.g., cassandra2.tmp
# 2. Add a location configuration. E.g., location = "/tmp/"
# 3. Copy and customize all other configurations.
# Make sure they are not the same if using the same store type!

15
weed/filer/cassandra/README.txt

@ -1,14 +1 @@
1. create a keyspace
CREATE KEYSPACE seaweedfs WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1};
2. create filemeta table
USE seaweedfs;
CREATE TABLE filemeta (
directory varchar,
name varchar,
meta blob,
PRIMARY KEY (directory, name)
) WITH CLUSTERING ORDER BY (name ASC);
Deprecated by cassandra2

15
weed/filer/cassandra2/README.txt

@ -0,0 +1,15 @@
1. create a keyspace
CREATE KEYSPACE seaweedfs WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1};
2. create filemeta table
USE seaweedfs;
CREATE TABLE filemeta (
dirhash bigint,
directory varchar,
name varchar,
meta blob,
PRIMARY KEY ((dirhash, directory), name)
) WITH CLUSTERING ORDER BY (name ASC);

221
weed/filer/cassandra2/cassandra_store.go

@ -0,0 +1,221 @@
package cassandra2
import (
"context"
"errors"
"fmt"
"github.com/gocql/gocql"
"time"
"github.com/seaweedfs/seaweedfs/weed/filer"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/util"
)
func init() {
filer.Stores = append(filer.Stores, &Cassandra2Store{})
}
type Cassandra2Store struct {
cluster *gocql.ClusterConfig
session *gocql.Session
superLargeDirectoryHash map[string]string
}
func (store *Cassandra2Store) GetName() string {
return "cassandra2"
}
func (store *Cassandra2Store) Initialize(configuration util.Configuration, prefix string) (err error) {
return store.initialize(
configuration.GetString(prefix+"keyspace"),
configuration.GetStringSlice(prefix+"hosts"),
configuration.GetString(prefix+"username"),
configuration.GetString(prefix+"password"),
configuration.GetStringSlice(prefix+"superLargeDirectories"),
configuration.GetString(prefix+"localDC"),
configuration.GetInt(prefix+"connection_timeout_millisecond"),
)
}
func (store *Cassandra2Store) isSuperLargeDirectory(dir string) (dirHash string, isSuperLargeDirectory bool) {
dirHash, isSuperLargeDirectory = store.superLargeDirectoryHash[dir]
return
}
func (store *Cassandra2Store) initialize(keyspace string, hosts []string, username string, password string, superLargeDirectories []string, localDC string, timeout int) (err error) {
store.cluster = gocql.NewCluster(hosts...)
if username != "" && password != "" {
store.cluster.Authenticator = gocql.PasswordAuthenticator{Username: username, Password: password}
}
store.cluster.Keyspace = keyspace
store.cluster.Timeout = time.Duration(timeout) * time.Millisecond
glog.V(0).Infof("timeout = %d", timeout)
fallback := gocql.RoundRobinHostPolicy()
if localDC != "" {
fallback = gocql.DCAwareRoundRobinPolicy(localDC)
}
store.cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(fallback)
store.cluster.Consistency = gocql.LocalQuorum
store.session, err = store.cluster.CreateSession()
if err != nil {
glog.V(0).Infof("Failed to open cassandra2 store, hosts %v, keyspace %s", hosts, keyspace)
}
// set directory hash
store.superLargeDirectoryHash = make(map[string]string)
existingHash := make(map[string]string)
for _, dir := range superLargeDirectories {
// adding dir hash to avoid duplicated names
dirHash := util.Md5String([]byte(dir))[:4]
store.superLargeDirectoryHash[dir] = dirHash
if existingDir, found := existingHash[dirHash]; found {
glog.Fatalf("directory %s has the same hash as %s", dir, existingDir)
}
existingHash[dirHash] = dir
}
return
}
func (store *Cassandra2Store) BeginTransaction(ctx context.Context) (context.Context, error) {
return ctx, nil
}
func (store *Cassandra2Store) CommitTransaction(ctx context.Context) error {
return nil
}
func (store *Cassandra2Store) RollbackTransaction(ctx context.Context) error {
return nil
}
func (store *Cassandra2Store) InsertEntry(ctx context.Context, entry *filer.Entry) (err error) {
dir, name := entry.FullPath.DirAndName()
if dirHash, ok := store.isSuperLargeDirectory(dir); ok {
dir, name = dirHash+name, ""
}
meta, err := entry.EncodeAttributesAndChunks()
if err != nil {
return fmt.Errorf("encode %s: %s", entry.FullPath, err)
}
if len(entry.GetChunks()) > filer.CountEntryChunksForGzip {
meta = util.MaybeGzipData(meta)
}
if err := store.session.Query(
"INSERT INTO filemeta (dirhash,directory,name,meta) VALUES(?,?,?,?) USING TTL ? ",
util.HashStringToLong(dir), dir, name, meta, entry.TtlSec).Exec(); err != nil {
return fmt.Errorf("insert %s: %s", entry.FullPath, err)
}
return nil
}
func (store *Cassandra2Store) UpdateEntry(ctx context.Context, entry *filer.Entry) (err error) {
return store.InsertEntry(ctx, entry)
}
func (store *Cassandra2Store) FindEntry(ctx context.Context, fullpath util.FullPath) (entry *filer.Entry, err error) {
dir, name := fullpath.DirAndName()
if dirHash, ok := store.isSuperLargeDirectory(dir); ok {
dir, name = dirHash+name, ""
}
var data []byte
if err := store.session.Query(
"SELECT meta FROM filemeta WHERE dirhash=? AND directory=? AND name=?",
util.HashStringToLong(dir), dir, name).Scan(&data); err != nil {
if errors.Is(err, gocql.ErrNotFound) {
return nil, filer_pb.ErrNotFound
}
return nil, err
}
entry = &filer.Entry{
FullPath: fullpath,
}
err = entry.DecodeAttributesAndChunks(util.MaybeDecompressData(data))
if err != nil {
return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err)
}
return entry, nil
}
func (store *Cassandra2Store) DeleteEntry(ctx context.Context, fullpath util.FullPath) error {
dir, name := fullpath.DirAndName()
if dirHash, ok := store.isSuperLargeDirectory(dir); ok {
dir, name = dirHash+name, ""
}
if err := store.session.Query(
"DELETE FROM filemeta WHERE dirhash=? AND directory=? AND name=?",
util.HashStringToLong(dir), dir, name).Exec(); err != nil {
return fmt.Errorf("delete %s : %v", fullpath, err)
}
return nil
}
func (store *Cassandra2Store) DeleteFolderChildren(ctx context.Context, fullpath util.FullPath) error {
if _, ok := store.isSuperLargeDirectory(string(fullpath)); ok {
return nil // filer.ErrUnsupportedSuperLargeDirectoryListing
}
if err := store.session.Query(
"DELETE FROM filemeta WHERE dirhash=? AND directory=?",
util.HashStringToLong(string(fullpath)), fullpath).Exec(); err != nil {
return fmt.Errorf("delete %s : %v", fullpath, err)
}
return nil
}
func (store *Cassandra2Store) ListDirectoryPrefixedEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) {
return lastFileName, filer.ErrUnsupportedListDirectoryPrefixed
}
func (store *Cassandra2Store) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) {
if _, ok := store.isSuperLargeDirectory(string(dirPath)); ok {
return // nil, filer.ErrUnsupportedSuperLargeDirectoryListing
}
cqlStr := "SELECT NAME, meta FROM filemeta WHERE dirhash=? AND directory=? AND name>? ORDER BY NAME ASC LIMIT ?"
if includeStartFile {
cqlStr = "SELECT NAME, meta FROM filemeta WHERE dirhash=? AND directory=? AND name>=? ORDER BY NAME ASC LIMIT ?"
}
var data []byte
var name string
iter := store.session.Query(cqlStr, util.HashStringToLong(string(dirPath)), string(dirPath), startFileName, limit+1).Iter()
for iter.Scan(&name, &data) {
entry := &filer.Entry{
FullPath: util.NewFullPath(string(dirPath), name),
}
lastFileName = name
if decodeErr := entry.DecodeAttributesAndChunks(util.MaybeDecompressData(data)); decodeErr != nil {
err = decodeErr
glog.V(0).Infof("list %s : %v", entry.FullPath, err)
break
}
if !eachEntryFunc(entry) {
break
}
}
if err = iter.Close(); err != nil {
glog.V(0).Infof("list iterator close: %v", err)
}
return lastFileName, err
}
func (store *Cassandra2Store) Shutdown() {
store.session.Close()
}

63
weed/filer/cassandra2/cassandra_store_kv.go

@ -0,0 +1,63 @@
package cassandra2
import (
"context"
"encoding/base64"
"fmt"
"github.com/gocql/gocql"
"github.com/seaweedfs/seaweedfs/weed/filer"
"github.com/seaweedfs/seaweedfs/weed/util"
)
func (store *Cassandra2Store) KvPut(ctx context.Context, key []byte, value []byte) (err error) {
dir, name := genDirAndName(key)
if err := store.session.Query(
"INSERT INTO filemeta (dirhash,directory,name,meta) VALUES(?,?,?,?) USING TTL ? ",
util.HashStringToLong(dir), dir, name, value, 0).Exec(); err != nil {
return fmt.Errorf("kv insert: %s", err)
}
return nil
}
func (store *Cassandra2Store) KvGet(ctx context.Context, key []byte) (data []byte, err error) {
dir, name := genDirAndName(key)
if err := store.session.Query(
"SELECT meta FROM filemeta WHERE dirhash=? AND directory=? AND name=?",
util.HashStringToLong(dir), dir, name).Scan(&data); err != nil {
if err != gocql.ErrNotFound {
return nil, filer.ErrKvNotFound
}
}
if len(data) == 0 {
return nil, filer.ErrKvNotFound
}
return data, nil
}
func (store *Cassandra2Store) KvDelete(ctx context.Context, key []byte) (err error) {
dir, name := genDirAndName(key)
if err := store.session.Query(
"DELETE FROM filemeta WHERE dirhash=? AND directory=? AND name=?",
util.HashStringToLong(dir), dir, name).Exec(); err != nil {
return fmt.Errorf("kv delete: %v", err)
}
return nil
}
func genDirAndName(key []byte) (dir string, name string) {
for len(key) < 8 {
key = append(key, 0)
}
dir = base64.StdEncoding.EncodeToString(key[:8])
name = base64.StdEncoding.EncodeToString(key[8:])
return
}

5
weed/filer/redis/README.md

@ -0,0 +1,5 @@
Deprecated by redis2.
This implementaiton uses unsorted set. For example, add a directory child via SAdd.
Redis2 moves to sorted set. Adding a child uses ZAddNX.

4
weed/filer/redis3/README.md

@ -0,0 +1,4 @@
Desuppported.
This implementation attempts to use skip list.
Did not get any report on actual benefits.
Loading…
Cancel
Save