Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MySQL backend registry #3850

Merged
merged 17 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ linters-settings:
alias: otelsdktrace
- pkg: go.opentelemetry.io/otel/semconv/v1.21.0
alias: otelsemconv
- pkg: github.com/FerretDB/FerretDB/internal/backends/mysql/metadata/pool
alias: mysqlpool
# ineffassign
lll:
line-length: 130
Expand Down
34 changes: 24 additions & 10 deletions cmd/envtool/envtool.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"go.uber.org/zap/zapcore"

"github.com/FerretDB/FerretDB/build/version"
mysqlpool "github.com/FerretDB/FerretDB/internal/backends/mysql/metadata/pool"
"github.com/FerretDB/FerretDB/internal/backends/postgresql/metadata/pool"
"github.com/FerretDB/FerretDB/internal/util/ctxutil"
"github.com/FerretDB/FerretDB/internal/util/debug"
Expand Down Expand Up @@ -147,32 +148,45 @@ func setupPostgresSecured(ctx context.Context, logger *zap.SugaredLogger) error

// setupMySQL configures `mysql` container.
func setupMySQL(ctx context.Context, logger *zap.SugaredLogger) error {
uri := "mysql://root:password@127.0.0.1:3306/ferretdb"

sp, err := state.NewProvider("")
if err != nil {
return err
}

if err := waitForPort(ctx, logger.Named("mysql"), 3306); err != nil {
return err
}

// we should replace with backend code similar to setupAnyPostgres
eval := "mysql -u root -ppassword -e \"GRANT ALL PRIVILEGES ON *.* TO 'username'@'%';\""
args := []string{"compose", "exec", "-T", "mysql", "bash", "-c", eval}
p, err := mysqlpool.New(uri, logger.Desugar(), sp)
if err != nil {
return err
}

var buf bytes.Buffer
var retry int64
defer p.Close()

var retry int64
for ctx.Err() == nil {
buf.Reset()

err := runCommand("docker", args, &buf, logger)
db, err := p.Get("root", "password")
if err == nil {
if _, rowErr := db.QueryContext(ctx, "GRANT ALL PRIVILEGES ON *.* TO 'username'@'%';"); rowErr != nil {
return lazyerrors.Error(err)
}
break
}

logger.Infof("%s:\n%s", err, buf.String())
logger.Infof("%s: %s", uri, err)

retry++
ctxutil.SleepWithJitter(ctx, time.Second, retry)
}

return ctx.Err()
if ctx.Err() != nil {
return ctx.Err()
}

return nil
}

// setupMongodb configures `mongodb` container.
Expand Down
17 changes: 17 additions & 0 deletions internal/backends/mysql/metadata/indexes.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import (
"errors"
"slices"

"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/iterator"
Expand All @@ -40,6 +41,22 @@
Descending bool
}

// deepCopy returns a deep copy.
func (indexes Indexes) deepCopy() Indexes {
res := make(Indexes, len(indexes))

for i, index := range indexes {
res[i] = IndexInfo{
Name: index.Name,
Index: index.Index,
Key: slices.Clone(index.Key),
Unique: index.Unique,
}
}

Check warning on line 55 in internal/backends/mysql/metadata/indexes.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/mysql/metadata/indexes.go#L45-L55

Added lines #L45 - L55 were not covered by tests

return res

Check warning on line 57 in internal/backends/mysql/metadata/indexes.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/mysql/metadata/indexes.go#L57

Added line #L57 was not covered by tests
}

// marshal returns [*types.Array] for indexes.
func (indexes Indexes) marshal() *types.Array {
res := types.MakeArray(len(indexes))
Expand Down
30 changes: 30 additions & 0 deletions internal/backends/mysql/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,27 @@
"database/sql"
"database/sql/driver"

"github.com/FerretDB/FerretDB/internal/backends"
"github.com/FerretDB/FerretDB/internal/handler/sjson"
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/internal/util/must"
)

const (
// DefaultColumn is a column name for all fields.
DefaultColumn = backends.ReservedPrefix + "sjson"

// IDColumn is a MySQL path expression for _id field.
IDColumn = DefaultColumn + "->'$._id'"

// TableIdxColumn is a column name for MySQL generated column.
TableIdxColumn = DefaultColumn + "_table"

// RecordIDColumn is a name for RecordID column to store capped collection record id.
RecordIDColumn = backends.ReservedPrefix + "record_id"
)

// Collection represents collection metadata.
//
// Collection value should be immutable to avoid data races.
Expand All @@ -37,6 +52,21 @@
CappedDocuments int64
}

// deepCopy returns a deep copy.
func (c *Collection) deepCopy() *Collection {
if c == nil {
return nil
}

Check warning on line 59 in internal/backends/mysql/metadata/metadata.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/mysql/metadata/metadata.go#L56-L59

Added lines #L56 - L59 were not covered by tests

return &Collection{
Name: c.Name,
TableName: c.TableName,
Indexes: c.Indexes.deepCopy(),
CappedSize: c.CappedSize,
CappedDocuments: c.CappedDocuments,
}

Check warning on line 67 in internal/backends/mysql/metadata/metadata.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/mysql/metadata/metadata.go#L61-L67

Added lines #L61 - L67 were not covered by tests
}

// Capped returns true if collection is capped.
func (c Collection) Capped() bool {
return c.CappedSize > 0
Expand Down
36 changes: 34 additions & 2 deletions internal/backends/mysql/metadata/pool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,23 @@

// New creates a new Pool.
func New(u string, l *zap.Logger, sp *state.Provider) (*Pool, error) {
return nil, lazyerrors.New("not yet implemented")
baseURI, err := url.Parse(u)
if err != nil {
return nil, lazyerrors.Error(err)
}

Check warning on line 58 in internal/backends/mysql/metadata/pool/pool.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/mysql/metadata/pool/pool.go#L57-L58

Added lines #L57 - L58 were not covered by tests

p := &Pool{
baseURI: *baseURI,
l: l,
sp: sp,
rw: sync.RWMutex{},
dbs: map[string]*fsql.DB{},
token: resource.NewToken(),
}

resource.Track(p, p.token)

return p, nil
}

// Close closes all connections in the pool.
Expand Down Expand Up @@ -98,7 +114,7 @@
p.rw.Lock()
defer p.rw.Unlock()

// a concurrent connectio might have created a pool already; check again
// a concurrent connection might have created a pool already; check again
if res = p.dbs[u]; res != nil {
p.l.Debug("Pool: found existing pool (after acquiring lock)", zap.String("username", username))
return res, nil
Expand All @@ -116,6 +132,22 @@
return res, nil
}

// GetAny returns a random open pool of connections to MySQL, or nil if none are available.
func (p *Pool) GetAny() *fsql.DB {
p.rw.RLock()
defer p.rw.RUnlock()

for _, db := range p.dbs {
p.l.Debug("Pool.GetAny: return existing pool")

return db
}

Check warning on line 144 in internal/backends/mysql/metadata/pool/pool.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/mysql/metadata/pool/pool.go#L136-L144

Added lines #L136 - L144 were not covered by tests

p.l.Debug("Pool.GetAny: no existing pools")

return nil

Check warning on line 148 in internal/backends/mysql/metadata/pool/pool.go

View check run for this annotation

Codecov / codecov/patch

internal/backends/mysql/metadata/pool/pool.go#L146-L148

Added lines #L146 - L148 were not covered by tests
}

// Describe implements Prometheus.Collector.
func (p *Pool) Describe(ch chan<- *prometheus.Desc) {
prometheus.DescribeByCollect(p, ch)
Expand Down
6 changes: 2 additions & 4 deletions internal/backends/mysql/metadata/pool/uri.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ package pool

import (
"net/url"
"path"
"strings"

"github.com/go-sql-driver/mysql"
)
Expand All @@ -36,8 +36,6 @@ func parseURI(uri string) (string, error) {
username := u.User.Username()
password, _ := u.User.Password()

dbName := path.Clean(u.Path)

values := u.Query()
params := make(map[string]string, len(values))

Expand All @@ -52,7 +50,7 @@ func parseURI(uri string) (string, error) {
Passwd: password,
Net: "tcp",
Addr: u.Host,
DBName: dbName,
DBName: strings.TrimPrefix(u.Path, "/"),
Params: params,
}
mysqlURL := cfg.FormatDSN()
Expand Down
Loading
Loading