github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/replication.go (about)

     1  // Copyright 2021 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package sqle
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  	"github.com/sirupsen/logrus"
    24  
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
    29  	"github.com/dolthub/dolt/go/store/types"
    30  )
    31  
    32  func getPushOnWriteHook(ctx context.Context, bThreads *sql.BackgroundThreads, dEnv *env.DoltEnv, logger io.Writer) (doltdb.CommitHook, error) {
    33  	_, val, ok := sql.SystemVariables.GetGlobal(dsess.ReplicateToRemote)
    34  	if !ok {
    35  		return nil, sql.ErrUnknownSystemVariable.New(dsess.ReplicateToRemote)
    36  	} else if val == "" {
    37  		return nil, nil
    38  	}
    39  
    40  	remoteName, ok := val.(string)
    41  	if !ok {
    42  		return nil, sql.ErrInvalidSystemVariableValue.New(val)
    43  	}
    44  
    45  	remotes, err := dEnv.GetRemotes()
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	rem, ok := remotes.Get(remoteName)
    51  	if !ok {
    52  		return nil, fmt.Errorf("%w: '%s'", env.ErrRemoteNotFound, remoteName)
    53  	}
    54  
    55  	ddb, err := rem.GetRemoteDB(ctx, types.Format_Default, dEnv)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	tmpDir, err := dEnv.TempTableFilesDir()
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	if _, val, ok = sql.SystemVariables.GetGlobal(dsess.AsyncReplication); ok && val == dsess.SysVarTrue {
    65  		return doltdb.NewAsyncPushOnWriteHook(bThreads, ddb, tmpDir, logger)
    66  	}
    67  
    68  	return doltdb.NewPushOnWriteHook(ddb, tmpDir), nil
    69  }
    70  
    71  // GetCommitHooks creates a list of hooks to execute on database commit. Hooks that cannot be created because of an
    72  // error in configuration will not prevent the server from starting, and will instead log errors.
    73  func GetCommitHooks(ctx context.Context, bThreads *sql.BackgroundThreads, dEnv *env.DoltEnv, logger io.Writer) ([]doltdb.CommitHook, error) {
    74  	postCommitHooks := make([]doltdb.CommitHook, 0)
    75  
    76  	hook, err := getPushOnWriteHook(ctx, bThreads, dEnv, logger)
    77  	if err != nil {
    78  		path, _ := dEnv.FS.Abs(".")
    79  		logrus.Errorf("error loading replication for database at %s, replication disabled: %v", path, err)
    80  		postCommitHooks = append(postCommitHooks, doltdb.NewLogHook([]byte(err.Error()+"\n")))
    81  	} else if hook != nil {
    82  		postCommitHooks = append(postCommitHooks, hook)
    83  	}
    84  
    85  	for _, h := range postCommitHooks {
    86  		_ = h.SetLogger(ctx, logger)
    87  	}
    88  	return postCommitHooks, nil
    89  }
    90  
    91  // newReplicaDatabase creates a new dsqle.ReadReplicaDatabase. If the doltdb.SkipReplicationErrorsKey global variable is set,
    92  // skip errors related to database construction only and return a partially functional dsqle.ReadReplicaDatabase
    93  // that will log warnings when attempting to perform replica commands.
    94  func newReplicaDatabase(ctx context.Context, name string, remoteName string, dEnv *env.DoltEnv) (ReadReplicaDatabase, error) {
    95  	opts := editor.Options{
    96  		Deaf: dEnv.DbEaFactory(),
    97  	}
    98  
    99  	db, err := NewDatabase(ctx, name, dEnv.DbData(), opts)
   100  	if err != nil {
   101  		return ReadReplicaDatabase{}, err
   102  	}
   103  
   104  	rrd, err := NewReadReplicaDatabase(ctx, db, remoteName, dEnv)
   105  	if err != nil {
   106  		err = fmt.Errorf("%s from remote '%s'; %w", ErrFailedToLoadReplicaDB.Error(), remoteName, err)
   107  		return ReadReplicaDatabase{}, err
   108  	}
   109  
   110  	if sqlCtx, ok := ctx.(*sql.Context); ok {
   111  		sqlCtx.GetLogger().Infof(
   112  			"replication enabled for database '%s' from remote '%s'", name, remoteName)
   113  	}
   114  
   115  	return rrd, nil
   116  }
   117  
   118  func ApplyReplicationConfig(ctx context.Context, bThreads *sql.BackgroundThreads, mrEnv *env.MultiRepoEnv, logger io.Writer, dbs ...dsess.SqlDatabase) ([]dsess.SqlDatabase, error) {
   119  	outputDbs := make([]dsess.SqlDatabase, len(dbs))
   120  	for i, db := range dbs {
   121  		dEnv := mrEnv.GetEnv(db.Name())
   122  		if dEnv == nil {
   123  			outputDbs[i] = db
   124  			continue
   125  		}
   126  		postCommitHooks, err := GetCommitHooks(ctx, bThreads, dEnv, logger)
   127  		if err != nil {
   128  			return nil, err
   129  		}
   130  		dEnv.DoltDB.SetCommitHooks(ctx, postCommitHooks)
   131  
   132  		if _, remote, ok := sql.SystemVariables.GetGlobal(dsess.ReadReplicaRemote); ok && remote != "" {
   133  			remoteName, ok := remote.(string)
   134  			if !ok {
   135  				return nil, sql.ErrInvalidSystemVariableValue.New(remote)
   136  			}
   137  			rdb, err := newReplicaDatabase(ctx, db.Name(), remoteName, dEnv)
   138  			if err == nil {
   139  				db = rdb
   140  			} else {
   141  				logrus.Errorf("invalid replication configuration, replication disabled: %v", err)
   142  			}
   143  		}
   144  
   145  		outputDbs[i] = db
   146  	}
   147  	return outputDbs, nil
   148  }