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 }