github.com/mailru/activerecord@v1.12.2/pkg/octopus/box.go (about)

     1  package octopus
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/mailru/activerecord/pkg/activerecord"
    10  	"github.com/mailru/activerecord/pkg/iproto/iproto"
    11  )
    12  
    13  var DefaultOptionCreator = func(sic activerecord.ShardInstanceConfig) (activerecord.OptionInterface, error) {
    14  	return NewOptions(
    15  		sic.Addr,
    16  		ServerModeType(sic.Mode),
    17  		WithTimeout(sic.Timeout, sic.Timeout),
    18  		WithPoolSize(sic.PoolSize),
    19  		WithPoolLogger(activerecord.IprotoLogger{}),
    20  	)
    21  }
    22  
    23  var DefaultConnectionParams = activerecord.MapGlobParam{
    24  	Timeout:  DefaultConnectionTimeout,
    25  	PoolSize: DefaultPoolSize,
    26  }
    27  
    28  // Box - возвращает коннектор для БД
    29  // TODO
    30  // - сделать статистику по используемым инстансам
    31  // - прикрутить локальный пингер и исключать недоступные инстансы
    32  func Box(ctx context.Context, shard int, instType activerecord.ShardInstanceType, configPath string, optionCreator func(activerecord.ShardInstanceConfig) (activerecord.OptionInterface, error)) (*Connection, error) {
    33  	if optionCreator == nil {
    34  		optionCreator = DefaultOptionCreator
    35  	}
    36  
    37  	clusterInfo, err := activerecord.ConfigCacher().Get(
    38  		ctx,
    39  		configPath,
    40  		DefaultConnectionParams,
    41  		optionCreator,
    42  	)
    43  	if err != nil {
    44  		return nil, fmt.Errorf("can't get cluster %s info: %w", configPath, err)
    45  	}
    46  
    47  	if clusterInfo.Shards() < shard {
    48  		return nil, fmt.Errorf("invalid shard num %d, max = %d", shard, clusterInfo.Shards())
    49  	}
    50  
    51  	var (
    52  		configBox activerecord.ShardInstance
    53  		ok        bool
    54  	)
    55  
    56  	switch instType {
    57  	case activerecord.ReplicaInstanceType:
    58  		configBox, ok = clusterInfo.NextReplica(shard)
    59  		if !ok {
    60  			return nil, fmt.Errorf("replicas not set")
    61  		}
    62  	case activerecord.ReplicaOrMasterInstanceType:
    63  		configBox, ok = clusterInfo.NextReplica(shard)
    64  		if ok {
    65  			break
    66  		}
    67  
    68  		fallthrough
    69  	case activerecord.MasterInstanceType:
    70  		configBox = clusterInfo.NextMaster(shard)
    71  	}
    72  
    73  	conn, err := activerecord.ConnectionCacher().GetOrAdd(configBox, func(options interface{}) (activerecord.ConnectionInterface, error) {
    74  		octopusOpt, ok := options.(*ConnectionOptions)
    75  		if !ok {
    76  			return nil, fmt.Errorf("invalit type of options %T, want Options", options)
    77  		}
    78  
    79  		return GetConnection(ctx, octopusOpt)
    80  	})
    81  	if err != nil {
    82  		return nil, fmt.Errorf("error from connectionCacher: %w", err)
    83  	}
    84  
    85  	box, ex := conn.(*Connection)
    86  	if !ex {
    87  		return nil, fmt.Errorf("invalid connection type %T, want *octopus.Connection", conn)
    88  	}
    89  
    90  	return box, nil
    91  }
    92  
    93  func CheckShardInstance(ctx context.Context, instance activerecord.ShardInstance) (activerecord.OptionInterface, error) {
    94  	c, err := activerecord.ConnectionCacher().GetOrAdd(instance, func(options interface{}) (activerecord.ConnectionInterface, error) {
    95  		octopusOpt, ok := options.(*ConnectionOptions)
    96  		if !ok {
    97  			return nil, fmt.Errorf("invalit type of options %T, want Options", options)
    98  		}
    99  
   100  		return GetConnection(ctx, octopusOpt)
   101  	})
   102  	if err != nil {
   103  		return nil, fmt.Errorf("error from connectionCacher: %w", err)
   104  	}
   105  
   106  	conn, ok := c.(*Connection)
   107  	if !ok {
   108  		return nil, fmt.Errorf("invalid connection type %T, want *octopus.Connection", conn)
   109  	}
   110  
   111  	if len(conn.pool.Online()) == 0 {
   112  		return nil, fmt.Errorf("no online channels")
   113  	}
   114  
   115  	td, err := CallLua(ctx, conn, "box.dostring", "return box.info.status")
   116  	if err != nil {
   117  		return nil, fmt.Errorf("can't get status: %w", err)
   118  	}
   119  
   120  	if len(td) == 1 {
   121  		ret := td[0]
   122  		switch string(ret.Data[0]) {
   123  		case "primary":
   124  			instance.Config.Mode = activerecord.ServerModeType(ModeMaster)
   125  		default:
   126  			instance.Config.Mode = activerecord.ServerModeType(ModeReplica)
   127  		}
   128  
   129  		return DefaultOptionCreator(instance.Config)
   130  	}
   131  
   132  	return nil, fmt.Errorf("can't parse status: %w", err)
   133  }
   134  
   135  func ProcessResp(respBytes []byte, cntFlag CountFlags) ([]TupleData, error) {
   136  	tupleCnt, respData, errResp := UnpackResopnseStatus(respBytes)
   137  	if errResp != nil {
   138  		return nil, fmt.Errorf("error response from box: `%w`", errResp)
   139  	}
   140  
   141  	if cntFlag&UniqRespFlag == UniqRespFlag && tupleCnt > 2 {
   142  		return nil, fmt.Errorf("returning more than one tuple: %d", tupleCnt)
   143  	}
   144  
   145  	if cntFlag&NeedRespFlag == NeedRespFlag && tupleCnt == 0 {
   146  		return nil, fmt.Errorf("empty tuple")
   147  	}
   148  
   149  	rdr := bytes.NewReader(respData)
   150  
   151  	var tuplesData []TupleData
   152  	tuplesData = make([]TupleData, 0, tupleCnt)
   153  
   154  	for f := 0; f < int(tupleCnt); f++ {
   155  		var tupleSize, fieldCnt, totalFieldLen uint32
   156  
   157  		if err := iproto.UnpackUint32(rdr, &tupleSize, iproto.ModeDefault); err != nil {
   158  			return nil, fmt.Errorf("error unpacking tuple '%w'", err)
   159  		}
   160  
   161  		if uint32(rdr.Len()) < tupleSize {
   162  			return nil, fmt.Errorf("error tuple(%d) size %d, need %d", f+1, rdr.Len(), tupleSize)
   163  		}
   164  
   165  		if err := iproto.UnpackUint32(rdr, &fieldCnt, iproto.ModeDefault); err != nil {
   166  			return nil, fmt.Errorf("error unpack fields cnt in tuple %d: %w", f, err)
   167  		}
   168  
   169  		td := TupleData{Cnt: fieldCnt}
   170  		td.Data = make([][]byte, 0, fieldCnt)
   171  
   172  		for ff := 0; ff < int(fieldCnt); ff++ {
   173  			var fieldLen uint32
   174  
   175  			err := iproto.UnpackUint32(rdr, &fieldLen, iproto.ModeBER)
   176  			if err != nil {
   177  				return nil, fmt.Errorf("error unpack fieldLen(%d) in tuple(%d): '%w'", ff, f, err)
   178  			}
   179  
   180  			if totalFieldLen+fieldLen > tupleSize {
   181  				return nil, fmt.Errorf("len fields overflow(%d) in tuple(%d)", totalFieldLen+fieldLen, f)
   182  			}
   183  
   184  			totalFieldLen += fieldLen
   185  			td.Data = append(td.Data, respData[len(respData)-rdr.Len():len(respData)-rdr.Len()+int(fieldLen)])
   186  
   187  			if _, err := rdr.Seek(int64(fieldLen), io.SeekCurrent); err != nil {
   188  				return nil, fmt.Errorf("can't seek^ %w", err)
   189  			}
   190  		}
   191  
   192  		tuplesData = append(tuplesData, td)
   193  	}
   194  
   195  	if rdr.Len() > 0 {
   196  		return nil, fmt.Errorf("extra data in resp: '%X'", respData[len(respData)-rdr.Len():])
   197  	}
   198  
   199  	return tuplesData, nil
   200  }
   201  
   202  func PackInsertReplace(ns uint32, insertMode InsertMode, tuple [][]byte) []byte {
   203  	w := make([]byte, 0, SpaceLen+FlagsLen+FieldNumLen+PackedTupleLen(tuple))
   204  
   205  	w = PackSpace(w, ns)
   206  	w = PackRequestFlagsVal(w, true, insertMode)
   207  	w = PackTuple(w, tuple)
   208  
   209  	return w
   210  }
   211  
   212  func UnpackInsertReplace(data []byte) (ns uint32, needRetVal bool, insertMode InsertMode, tuple [][]byte, err error) {
   213  	rdr := bytes.NewReader(data)
   214  
   215  	ns, err = UnpackSpace(rdr)
   216  	if err != nil {
   217  		return
   218  	}
   219  
   220  	needRetVal, insertMode, err = UnpackRequestFlagsVal(rdr) // Always true, 0, see PackUpdate
   221  	if err != nil {
   222  		err = fmt.Errorf("can't unpack flags: %s", err)
   223  		return
   224  	}
   225  
   226  	tuple, err = UnpackTuple(rdr)
   227  	if err != nil {
   228  		err = fmt.Errorf("can't unpack insert tuple: %s", err)
   229  		return
   230  	}
   231  
   232  	return
   233  }
   234  
   235  func PackSelect(ns, indexnum, offset, limit uint32, keys [][][]byte) []byte {
   236  	w := make([]byte, 0, SpaceLen+IndexLen+OffsetLen+LimitLen+PackedTuplesLen(keys))
   237  
   238  	w = PackSpace(w, ns)
   239  	w = PackIndexNum(w, indexnum)
   240  	w = PackOffset(w, offset)
   241  	w = PackLimit(w, limit)
   242  	w = PackTuples(w, keys)
   243  
   244  	return w
   245  }
   246  
   247  func PackUpdate(ns uint32, primaryKey [][]byte, updateOps []Ops) []byte {
   248  	w := make([]byte, 0, SpaceLen+FlagsLen+PackedKeyLen(primaryKey)+PackedUpdateOpsLen(updateOps))
   249  
   250  	w = PackSpace(w, ns)
   251  	w = PackRequestFlagsVal(w, true, 0)
   252  	w = PackKey(w, primaryKey)
   253  
   254  	if len(updateOps) != 0 {
   255  		w = iproto.PackUint32(w, uint32(len(updateOps)), iproto.ModeDefault)
   256  
   257  		for _, op := range updateOps {
   258  			w = iproto.PackUint32(w, op.Field, iproto.ModeDefault)
   259  			w = append(w, byte(op.Op))
   260  			w = iproto.PackBytes(w, op.Value, iproto.ModeBER)
   261  		}
   262  	}
   263  
   264  	return w
   265  }
   266  
   267  func UnpackUpdate(data []byte) (ns uint32, primaryKey [][]byte, updateOps []Ops, err error) {
   268  	rdr := bytes.NewReader(data)
   269  
   270  	ns, err = UnpackSpace(rdr)
   271  	if err != nil {
   272  		return
   273  	}
   274  
   275  	_, _, err = UnpackRequestFlagsVal(rdr) // Always true, 0, see PackUpdate
   276  	if err != nil {
   277  		err = fmt.Errorf("can't unpack flags: %s", err)
   278  		return
   279  	}
   280  
   281  	primaryKey, err = UnpackKey(rdr)
   282  	if err != nil {
   283  		err = fmt.Errorf("can't unpack PK: %s", err)
   284  		return
   285  	}
   286  
   287  	if rdr.Len() != 0 {
   288  		numUpdate := uint32(0)
   289  
   290  		err = iproto.UnpackUint32(rdr, &numUpdate, iproto.ModeDefault)
   291  		if err != nil {
   292  			err = fmt.Errorf("can't unpack updateOps len")
   293  			return
   294  		}
   295  
   296  		updateOps = make([]Ops, 0, numUpdate)
   297  
   298  		for f := 0; f < int(numUpdate); f++ {
   299  			op := Ops{}
   300  
   301  			err = iproto.UnpackUint32(rdr, &op.Field, iproto.ModeDefault)
   302  			if err != nil {
   303  				err = fmt.Errorf("can't unpack field name from updateops (%d): %s", f, err)
   304  				return
   305  			}
   306  
   307  			opCode, errOp := rdr.ReadByte()
   308  			if err != nil {
   309  				err = fmt.Errorf("can't unpack opCode from updateops (%d): %s", f, errOp)
   310  				return
   311  			}
   312  
   313  			op.Op = OpCode(opCode)
   314  
   315  			err = iproto.UnpackBytes(rdr, &op.Value, iproto.ModeBER)
   316  			if err != nil {
   317  				err = fmt.Errorf("can't unpack field value from updateops (%d): %s", f, err)
   318  				return
   319  			}
   320  
   321  			updateOps = append(updateOps, op)
   322  		}
   323  	}
   324  
   325  	return
   326  }
   327  
   328  func PackDelete(ns uint32, primaryKey [][]byte) []byte {
   329  	w := make([]byte, 0, SpaceLen+FlagsLen+PackedKeysLen(primaryKey))
   330  
   331  	w = PackSpace(w, ns)
   332  	w = PackRequestFlagsVal(w, true, 0)
   333  	w = PackKey(w, primaryKey)
   334  
   335  	return w
   336  }
   337  
   338  func UnpackDelete(data []byte) (ns uint32, primaryKey [][]byte, err error) {
   339  	rdr := bytes.NewReader(data)
   340  
   341  	ns, err = UnpackSpace(rdr)
   342  	if err != nil {
   343  		return
   344  	}
   345  
   346  	_, _, err = UnpackRequestFlagsVal(rdr) // Always true, 0, see PackDelete
   347  	if err != nil {
   348  		err = fmt.Errorf("can't unpack flags: %s", err)
   349  		return
   350  	}
   351  
   352  	primaryKey, err = UnpackKey(rdr)
   353  	if err != nil {
   354  		err = fmt.Errorf("can't unpack PK: %s", err)
   355  		return
   356  	}
   357  
   358  	return
   359  }
   360  
   361  func PackLua(name string, args ...string) []byte {
   362  	w := iproto.PackUint32([]byte{}, 0, iproto.ModeDefault)         // Всегда константа 0
   363  	w = iproto.PackBytes(w, []byte(name), iproto.ModeBER)           // Название lua процедуры с длинной в BER формате
   364  	w = iproto.PackUint32(w, uint32(len(args)), iproto.ModeDefault) // Количество аргументов
   365  
   366  	for _, arg := range args {
   367  		w = iproto.PackBytes(w, []byte(arg), iproto.ModeBER) // Аргументы с длинной в BER вормате
   368  	}
   369  
   370  	return w
   371  }
   372  
   373  func UnpackLua(data []byte) (name string, args [][]byte, err error) {
   374  	r := bytes.NewReader(data)
   375  
   376  	var v uint32
   377  
   378  	if err = iproto.UnpackUint32(r, &v, iproto.ModeDefault); err != nil || v != 0 {
   379  		return name, args, fmt.Errorf("can't unpack as call lua procedure: %w", err)
   380  	}
   381  
   382  	var procName []byte
   383  
   384  	if err = iproto.UnpackBytes(r, &procName, iproto.ModeBER); err != nil {
   385  		return name, args, fmt.Errorf("can't unpack lua procedure name: %w", err)
   386  	}
   387  
   388  	if args, err = UnpackTuple(r); err != nil {
   389  		err = fmt.Errorf("can't unpack lua procedure args: %s", err)
   390  		return
   391  	}
   392  
   393  	return string(procName), args, nil
   394  
   395  }