github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/function/ctl/cmd_merge.go (about) 1 // Copyright 2021 - 2022 Matrix Origin 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 ctl 16 17 import ( 18 "errors" 19 "strconv" 20 "strings" 21 22 "github.com/docker/go-units" 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 "github.com/matrixorigin/matrixone/pkg/container/types" 25 "github.com/matrixorigin/matrixone/pkg/logutil" 26 "github.com/matrixorigin/matrixone/pkg/objectio" 27 "github.com/matrixorigin/matrixone/pkg/pb/api" 28 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 29 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db" 30 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/merge" 31 "github.com/matrixorigin/matrixone/pkg/vm/process" 32 "go.uber.org/zap" 33 ) 34 35 const defaultTargetObjectSize = 120 * common.Const1MBytes 36 37 type arguments struct { 38 db, tbl string 39 objs []objectio.ObjectStats 40 filter string 41 targetObjSize int 42 } 43 44 // Args: 45 // 46 // "dbName.tableName[:obj1,obj2,...:targetObjSize]" 47 // "dbName.tableName[:all:filter:targetObjSize]" 48 // 49 // filter: "overlap", "small" 50 // filter default: "basic" 51 // targetObjSize: "1G", "1M", "1K" 52 // targetObjSize default: "120M" 53 // Example: "db1.tbl1" 54 // Example: "db1.tbl1:all:small:100M" 55 // Example: "db1.tbl1:obj1,obj2:100M" 56 func parseArgs(arg string) (arguments, error) { 57 args := strings.Split(arg, ":") 58 if len(args) < 1 || len(args) > 4 { 59 return arguments{}, moerr.NewInternalErrorNoCtx("handleMerge: invalid arg format") 60 } 61 62 a := arguments{ 63 targetObjSize: defaultTargetObjectSize, 64 } 65 66 // Parse db and table 67 dbtbl := strings.Split(args[0], ".") 68 if len(dbtbl) != 2 { 69 return arguments{}, moerr.NewInternalErrorNoCtx("handleMerge: invalid db.table format") 70 } 71 a.db, a.tbl = dbtbl[0], dbtbl[1] 72 73 // Parse objects 74 if len(args) != 1 && args[1] != "all" { 75 objs := strings.Split(args[1], ",") 76 a.objs = make([]objectio.ObjectStats, 0, len(objs)) 77 for _, objStr := range objs { 78 // Parse object 79 objStr = strings.TrimSpace(objStr) 80 parts := strings.Split(objStr, "_") 81 if len(parts) != 2 { 82 return arguments{}, moerr.NewInternalErrorNoCtx("handleMerge: invalid obj format: %s", objStr) 83 } 84 uuid, err := types.ParseUuid(parts[0]) 85 if err != nil { 86 return arguments{}, errors.Join(moerr.NewInternalErrorNoCtx("handleMerge: invalid obj uuid format: %s", objStr), err) 87 } 88 num, err := strconv.Atoi(parts[1]) 89 if err != nil { 90 return arguments{}, errors.Join(moerr.NewInternalErrorNoCtx("handleMerge: invalid obj num format: %s", objStr), err) 91 } 92 objectname := objectio.BuildObjectName(&uuid, uint16(num)) 93 obj := objectio.NewObjectStats() 94 objectio.SetObjectStatsObjectName(obj, objectname) 95 a.objs = append(a.objs, *obj) 96 } 97 } 98 99 if len(args) <= 2 { 100 return a, nil 101 } 102 103 if args[1] == "all" { 104 // Parse filter 105 // only parse filter if obj is not specified 106 a.filter = strings.TrimSpace(strings.ToLower(args[2])) 107 if len(args) == 3 { 108 return a, nil 109 } 110 // Parse targetObjSize 111 size, err := units.RAMInBytes(args[3]) 112 if err != nil { 113 return arguments{}, errors.Join(moerr.NewInternalErrorNoCtx("handleMerge: invalid targetObjSize format: %s", args[3]), err) 114 } 115 a.targetObjSize = int(size) 116 } else { 117 // Parse targetObjSize 118 size, err := units.RAMInBytes(args[2]) 119 if err != nil { 120 return arguments{}, errors.Join(moerr.NewInternalErrorNoCtx("handleMerge: invalid targetObjSize format: %s", args[3]), err) 121 } 122 a.targetObjSize = int(size) 123 } 124 125 return a, nil 126 } 127 128 func handleMerge() handleFunc { 129 return GetTNHandlerFunc( 130 api.OpCode_OpCommitMerge, 131 func(_ string) ([]uint64, error) { 132 return nil, nil 133 }, 134 func(tnShardID uint64, parameter string, proc *process.Process) (payload []byte, err error) { 135 txnOp := proc.TxnOperator 136 if proc.TxnOperator == nil { 137 return nil, moerr.NewInternalError(proc.Ctx, "handleFlush: txn operator is nil") 138 } 139 a, err := parseArgs(parameter) 140 if err != nil { 141 return nil, err 142 } 143 defer func() { 144 if err != nil { 145 logutil.Error("mergeblocks err on cn", 146 zap.String("err", err.Error()), zap.String("parameter", parameter)) 147 e := &api.MergeCommitEntry{ 148 StartTs: txnOp.SnapshotTS(), 149 Err: err.Error(), 150 } 151 for _, o := range a.objs { 152 e.MergedObjs = append(e.MergedObjs, o.Clone().Marshal()) 153 } 154 // No matter success or not, we should return the merge result to DN 155 payload, _ = e.MarshalBinary() 156 err = nil 157 } 158 }() 159 database, err := proc.SessionInfo.StorageEngine.Database(proc.Ctx, a.db, txnOp) 160 if err != nil { 161 logutil.Errorf("mergeblocks err on cn, db %s, err %s", a.db, err.Error()) 162 return nil, err 163 } 164 rel, err := database.Relation(proc.Ctx, a.tbl, nil) 165 if err != nil { 166 logutil.Errorf("mergeblocks err on cn, table %s, err %s", a.db, err.Error()) 167 return nil, err 168 } 169 entry, err := rel.MergeObjects(proc.Ctx, a.objs, a.filter, uint32(a.targetObjSize)) 170 if err != nil { 171 merge.CleanUpUselessFiles(entry, proc.FileService) 172 return nil, err 173 } 174 payload, err = entry.MarshalBinary() 175 if err != nil { 176 return nil, err 177 } 178 return payload, nil 179 }, 180 func(data []byte) (any, error) { 181 resp := &db.InspectResp{} 182 if err := types.Decode(data, resp); err != nil { 183 return nil, err 184 } 185 return resp, nil 186 }) 187 }