From 9959bc645422006f59c1a7574ac54c66c751fc16 Mon Sep 17 00:00:00 2001 From: luyomo Date: Fri, 29 Nov 2024 10:51:11 +0900 Subject: [PATCH] Push the changes --- doc/filebeat.org | 6 + doc/percona-playback.org | 64 ++++ doc/pt-upgrade.org | 5 + doc/query-replayer.org | 8 + .../png/lightning_backend.png | Bin 0 -> 47100 bytes doc/read-source-code/png/region_split.png | Bin 0 -> 54701 bytes doc/read-source-code/tidb-lightning.org | 344 ++++++++++++++++++ embed/templates/config/aws-tidb-low.yaml | 33 +- embed/templates/config/tidb-cluster.yml | 2 +- embed/templates/config/tidb-cluster.yml.tpl | 2 +- .../config/tidb-cluster.yml.tpl.label | 2 +- .../config/tidb-cluster.yml.tpl.nolabel | 2 +- pkg/aws/manager/tidb.go | 270 ++++++++++++-- pkg/aws/manager/workstation.go | 208 ++++++----- terraform/tidb-on-aks/masterdb/main.tf | 2 +- 15 files changed, 819 insertions(+), 129 deletions(-) create mode 100644 doc/filebeat.org create mode 100644 doc/percona-playback.org create mode 100644 doc/pt-upgrade.org create mode 100644 doc/query-replayer.org create mode 100644 doc/read-source-code/png/lightning_backend.png create mode 100644 doc/read-source-code/png/region_split.png create mode 100644 doc/read-source-code/tidb-lightning.org diff --git a/doc/filebeat.org b/doc/filebeat.org new file mode 100644 index 00000000..08487b25 --- /dev/null +++ b/doc/filebeat.org @@ -0,0 +1,6 @@ +* autodiscover + + https://github.com/elastic/beats.git + -> libbeat/autodiscover/providers/kubernetes/kubernetes.go: init() + -> libbeat/autodiscover/providers/kubernetes/kubernetes.go: AutodiscoverBuilder + -> libbeat/autodiscover/providers/kubernetes/kubernetes.go: NewEventerManager + -> libbeat/autodiscover/providers/kubernetes/pod.go: NewPodEventer diff --git a/doc/percona-playback.org b/doc/percona-playback.org new file mode 100644 index 00000000..2f985d74 --- /dev/null +++ b/doc/percona-playback.org @@ -0,0 +1,64 @@ +* Description +** Option 01 + + Install MariaDB + + Install PDNS + + Install TiDB + + +** Option 02 + + MariaDB install + + Slow query setup + + TPCH run + + Data migration from mariadb to TiDB + + Playback Run On TiDB + + Report check +* Install + Compile the lib from docker image because the dependency is absolute old. +** docker install + #+BEGIN_SRC +workstation$ sudo apt-get install docker.io +workstation$ sudo chmod -aG docker user +workstation$ docker ps + #+END_SRC +** Compile the source code +#+BEGIN_SRC +workstation$ docker pull centos:7 +workstation$ docker run -it -v $(pwd)/output:/opt/output centos:7 bash +root@centos$ yum update -y +root@centos$ yum -y install tbb tbb-devel cmake boost boost-devel make git gcc-c++.x86_64 mariadb-devel.x86_64 +root@centos$ git clone https://github.com/Percona-Lab/query-playback.git +root@centos$ cd query-playback/ +root@centos$ mkdir build_dir +root@centos$ cd build_dir +root@centos$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. +root@centos$ make +root@centos$ cp percona-playback /opt/output/ +root@centos$ exit +workstation$ ls output +percona-playback +#+END_SRC +* MARIADB Install +* PDNS Install +* Slow query setup +* Run percona-playback + #+BEGIN_SRC +sudo apt-get install libssl-dev +cd /lib/x86_64-linux-gnu +sudo ln -s libssl.so.1.0.0 libssl.so.10 +sudo ln -s libcrypto.so.1.0.0 libcrypto.so.10 + + +wget http://snapshot.debian.org/archive/debian/20190501T215844Z/pool/main/g/glibc/multiarch-support_2.28-10_amd64.deb +sudo dpkg -i multiarch-support*.deb + +wget http://snapshot.debian.org/archive/debian/20170705T160707Z/pool/main/o/openssl/libssl1.0.0_1.0.2l-1%7Ebpo8%2B1_amd64.deb +sudo dpkg -i libssl1.0.0*.deb + + +workstation$ cd /usr/lib/x86_64-linux-gnu +workstation$ sudo ln -s libmysqlclient.so libmysqlclient.so.18 + + + + + + #+END_SRC diff --git a/doc/pt-upgrade.org b/doc/pt-upgrade.org new file mode 100644 index 00000000..3d0472b5 --- /dev/null +++ b/doc/pt-upgrade.org @@ -0,0 +1,5 @@ +* Install + #+BEGIN_SRC + $ wget https://downloads.percona.com/downloads/percona-distribution-mysql-ps/percona-distribution-mysql-ps-8.3.0/binary/tarball/percona-toolkit-3.5.7 +_x86_64.tar.gz + #+END_SRC diff --git a/doc/query-replayer.org b/doc/query-replayer.org new file mode 100644 index 00000000..b3b8b98d --- /dev/null +++ b/doc/query-replayer.org @@ -0,0 +1,8 @@ +* [[https://weblabo.oscasierra.net/docker-centos7/][Pull centos docker image]] +* [[https://serverfault.com/questions/904304/could-not-resolve-host-mirrorlist-centos-org-centos-7][Replace yum repo]] +* Install procedure + #+BEGIN_SRC +$ yum -y install tbb tbb-devel cmake boost boost-devel +$ yum install cmake make gcc gcc-c++ +$ yum install -y mariadb-devel.x86_64 + #+END_SRC diff --git a/doc/read-source-code/png/lightning_backend.png b/doc/read-source-code/png/lightning_backend.png new file mode 100644 index 0000000000000000000000000000000000000000..347a72c595d30edffd8f2992e9c6d269e36ea1ea GIT binary patch literal 47100 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 literal 0 HcmV?d00001 diff --git a/doc/read-source-code/png/region_split.png b/doc/read-source-code/png/region_split.png new file mode 100644 index 0000000000000000000000000000000000000000..14e492decb9278f81beec0326e9ddcf15f4a6f16 GIT binary patch literal 54701 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�)fl__qV4avHUy6JydiT0HaInI6v1 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 z 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 Vcl#(Jposei6GP1LT>W#Q{|5W={38GW literal 0 HcmV?d00001 diff --git a/doc/read-source-code/tidb-lightning.org b/doc/read-source-code/tidb-lightning.org new file mode 100644 index 00000000..0d407748 --- /dev/null +++ b/doc/read-source-code/tidb-lightning.org @@ -0,0 +1,344 @@ +* Flow + + Read config file + + app.RunOnceWithOptions(lightning/lightning.go) + + run(lighting/lighting.go) +** Failed to fetch store info +*** Source Code + + lightning/backend/local/local.go + executeJob -> store, err = local.pdCtl.GetStoreInfo(ctx, peer.StoreId) + + pdutil/pd.go + GetStoreInfo -> getStoreInfoWith + #+BEGIN_SRC +workstation$ curl http://10.128.0.21:12379/pd/api/v1/store/133013 + #+END_SRC + +** region scan failed +*** Data + + lightning/backend/local/local.go(readAndSplitIntoRange) + #+BEGIN_SRC + start = 7480000000000000665F728000000000000001 + end = 7480000000000000665F728000000000000006 + #+END_SRC + + Before encode + #+BEGIN_SRC + start = 7480000000000000 665F728000000000 000001 + end = 7480000000000000 665F728000000000 000006 + pair start = 7480000000000000 665F728000000000 000001 + pair end = 7480000000000000 665F728000000000 000005 +encoded key = 7480000000000000FF665F728000000000FF0000010000000000FA +encoded key = 7480000000000000FF665F728000000000FF0000060000000000FA + #+END_SRC + + After encode +*** Source code + + lightning/backend/local/local.go + regions, err := split.PaginateScanRegion(ctx, local.splitCli, startKey, endKey, scanRegionLimit) + + restore/split/split.go + batch, err = client.ScanRegions(ctx, scanStartKey, endKey, limit) + + restore/split/client.go (ScanRegions) + regions, err := c.client.ScanRegions(ctx, key, endKey, limit) + + + +** Table_import +*** Controller + #+BEGIN_SRC +&importer.Controller{ + taskCtx:(*context.cancelCtx)(0xc000e1e820) + , cfg:(*config.Config)(0xc000e14000) + , dbMetas:[]*mydump.MDDatabaseMeta{(*mydump.MDDatabaseMeta)(0xc0006fa4b0)} + , dbInfos:map[string]*checkpoints.TidbDBInfo{ + "test":(*checkpoints.TidbDBInfo)(0xc000629020)} + , tableWorkers:(*worker.Pool)(0xc00ca4ecf0) + , indexWorkers:(*worker.Pool)(0xc00ca4ed50) + , regionWorkers:(*worker.Pool)(0xc00091ad50) + , ioWorkers:(*worker.Pool)(0xc00091ac30) + , checksumWorks:(*worker.Pool)(0xc00091adb0) + , pauser:(*common.Pauser)(0x8d6d1d0) + , engineMgr:backend.EngineManager{backend:(*local.Backend)(0xc0008b2e00)} + , backend:(*local.Backend)(0xc0008b2e00) + , db:(*sql.DB)(0xc000dbc750) + , pdCli:(*pd.client)(0xc000e2a0c0) + , alterTableLock:sync.Mutex{state:0, sema:0x0} + , sysVars:map[string]string{ + "block_encryption_mode":"aes-128-ecb" + , "default_week_format":"0" + , "div_precision_increment":"4" + , "group_concat_max_len":"1024" + , "lc_time_names":"en_US" + , "max_allowed_packet":"67108864" + , "tidb_backoff_weight":"2" + , "tidb_row_format_version":"2" + , "time_zone":"SYSTEM"} + , tls:(*common.TLS)(0xc000e60640) + , checkTemplate:(*importer.SimpleTemplate)(0xc000ef8a20) + , errorSummaries:importer.errorSummaries{ + Mutex:sync.Mutex{state:0, sema:0x0} + , logger:log.Logger{Logger:(*zap.Logger)(0xc000da30a0)} + , summary:map[string]importer.errorSummary{}} + , checkpointsDB:(*checkpoints.FileCheckpointsDB)(0xc0008ca060) + , saveCpCh:(chan importer.saveCp)(0xc000948180) + , checkpointsWg:sync.WaitGroup{ + noCopy:sync.noCopy{} + , state:atomic.Uint64{_:atomic.noCopy{} + , _:atomic.align64{} + , v:0x100000000 } + , sema:0x0} + , closedEngineLimit:(*worker.Pool)(0xc00091ae40) + , addIndexLimit:(*worker.Pool)(0xc00091aea0) + , store:(*storage.LocalStorage)(0xc000e57260) + , ownStore:true + , metaMgrBuilder:importer.singleMgrBuilder{taskID:1710909109939848971} + , errorMgr:(*errormanager.ErrorManager)(0xc000dc7780) + , taskMgr:(*importer.singleTaskMetaMgr)(0xc000dab540) + , diskQuotaLock:sync.RWMutex{ + w:sync.Mutex{state:0, sema:0x0} + , writerSem:0x0 + , readerSem:0x0 + , readerCount:atomic.Int32{_:atomic.noCopy{}, v:0} + , readerWait:atomic.Int32{_:atomic.noCopy{}, v:0}} + , diskQuotaState:atomic.Int32{_:atomic.nocmp{}, v:0} + , compactState:atomic.Int32{_:atomic.nocmp{}, v:0} + , status:(*importer.LightningStatus)(0xc000dca470) + , dupIndicator:(*atomic.Bool)(nil) + , preInfoGetter:(*importer.PreImportInfoGetterImpl)(0xc000ee5680) + , precheckItemBuilder:(*importer.PrecheckItemBuilder)(0xc000ee3360) + , encBuilder:(*local.encodingBuilder)(0xc000b35478) + , tikvModeSwitcher:(*local.switcher)(0xc000df76c0) + , keyspaceName:"" + , resourceGroupName:"rg1" + , taskType:"lightning"} + #+END_SRC +*** Check point + #+BEGIN_SRC +checkpoints.TableCheckpoint{ + Status:0x1e + , AllocBase:0 + , Engines:map[int32]*checkpoints.EngineCheckpoint{} + , TableID:102 + , TableInfo:(*model.TableInfo)(0xc00a038380) + , Checksum:verification.KVChecksum{base:0x0, prefixLen:0, bytes:0x0, kvs:0x0, checksum:0x0} +} + #+END_SRC + + + Checkpoint Status + | Name | Value | + |---------------------------------+-------| + | CheckpointStatusMissing | 0 | + | CheckpointStatusMaxInvalid | 25 | + | CheckpointStatusLoaded | 30 | + | CheckpointStatusAllWritten | 60 | + | CheckpointStatusDupDetected | 70 | + | CheckpointStatusIndexDropped | 80 | + | CheckpointStatusClosed | 90 | + | CheckpointStatusImported | 120 | + | CheckpointStatusIndexImported | 140 | + | CheckpointStatusAlteredAutoInc | 150 | + | CheckpointStatusChecksumSkipped | 170 | + | CheckpointStatusChecksummed | 180 | + | CheckpointStatusIndexAdded | 190 | + | CheckpointStatusAnalyzeSkipped | 200 | + | CheckpointStatusAnalyzed | 210 | + + + +** DataDivideConfig + #+BEGIN_SRC + "divide config"="&mydump.DataDivideConfig{ + ColumnCnt:2 + , EngineDataSize:0 + , MaxChunkSize:268435456 + , Concurrency:6 + , EngineConcurrency:6 + , BatchImportRatio:0.75 + , IOWorkers:(*worker.Pool)(0xc0004c9dd0) + , Store:(*storage.LocalStorage)(0xc000e92eb0) + , TableMeta:(*mydump.MDTableMeta)(0xc00076b900) + , StrictFormat:false + , DataCharacterSet:\"binary\" + , DataInvalidCharReplace:\"\", + , ReadBlockSize:65536 + , CSV:config.CSVConfig{ + Separator:"," + , Delimiter:\"\\\"\" + , Terminator:\"\" + , Null:config.StringOrStringSlice{\"\\\\N\"} + , Header:false + , HeaderSchemaMatch:true + , TrimLastSep:false + , NotNull:false + , BackslashEscape:true + , EscapedBy:\"\\\\\" + , StartingBy:\"\" + , AllowEmptyLine:false + , QuotedNullIsText:false + , UnescapedQuote:false}} + #+END_SRC +** Table Region + #+BEGIN_SRC + &mydump.TableRegion{ + EngineID:0 + , DB:"test" + , Table:"test01" + , FileMeta:mydump.SourceFileMeta{ + Path:"test.test01.csv" + , Type:4 + , Compression:0 + , SortKey:"" + , FileSize:20 + , ExtendData:mydump.ExtendColumnData{ + Columns:[]string(nil) + , Values:[]string(nil)} + , RealSize:20 + , Rows:0 + } + , ExtendData:mydump.ExtendColumnData{Columns:[]string(nil), Values:[]string(nil)} + , Chunk:mydump.Chunk{ + Offset:0 + , EndOffset:20 + , RealOffset:0 + , PrevRowIDMax:0, RowIDMax:10, Columns:[]string(nil)} + } + #+END_SRC +** version string + #+BEGIN_SRC +Release Version: v7.5.0-1-g169c811715-dirty\nEdition: Community\nGit Commit Hash: 169c81171584eef924b88fa16a6b946823205cff\nGit Branch: v7.5.0-rename\nUTC Build Time: 2024-03-09 14:43:51\nGoVersion: go1.21.5\nRace Enabled: false\nCheck Table Before Drop: false\nStore: tikv + #+END_SRC +** version info + #+BEGIN_SRC +version.ServerInfo{ServerType:3, ServerVersion:(*semver.Version)(0xc000fabc00), HasTiKV:false} + +"Server version"="&semver.Version{ + Major:7 + , Minor:5 + , Patch:0 + , PreRelease:\"1-g169c811715-dirty\" + , Metadata:\"\"}" + #+END_SRC + +** importer/meta_manager.go:AllocTableRowIDs + How to populate the data into task_meta_v2? +* Flow data import + #+BEGIN_SRC +// 1. Create a `Backend` for the whole process. +// 2. For each table, +// i. Split into multiple "batches" consisting of data files with roughly equal total size. +// ii. For each batch, +// a. Create an `OpenedEngine` via `backend.OpenEngine()` +// b. For each chunk, deliver data into the engine via `engine.WriteRows()` +// c. When all chunks are written, obtain a `ClosedEngine` via `engine.Close()` +// d. Import data via `engine.Import()` +// e. Cleanup via `engine.Cleanup()` +// 3. Close the connection via `backend.Close()` + #+END_SRC + + + importer/table_import.go + + dataClosedEngine, err := tr.preprocessEngine(ctx, rc, indexEngine, eid, ecp) + + err := cr.process(ctx, tr, engineID, dataWriter, indexWriter, rc) + + importer/chunk_process.go:newChunkProcessor + importer/chunk_process.go:openParser + +* Summary +** Calling Flow + + importer/import.go: Run + #+BEGIN_SRC plantuml :file ./png/lightning_backend.png +participant Controller_Run [ + =Controller + ---- + ""Run"" +] + +participant Controller + +participant TableImporter + +participant TableImporter_importEngines [ + =TableImporter + ---- + ""importEngines"" +] + +participant TableImporter_preprocessEngine [ + =TableImporter + ---- + ""preprocessEngine"" +] + +Controller_Run->Controller: setGlobalVariables +Controller_Run->Controller: restoreSchema +Controller_Run->Controller: restoreSchema +Controller_Run->Controller: preCheckRequirements +Controller_Run->Controller: initCheckpoint +Controller_Run->Controller: importTables +Controller->TableImporter: importTable +TableImporter->EngineManager: OpenEngine +EngineManager->Backend: OpenEngine(important) +TableImporter->TableImporter_importEngines +TableImporter_importEngines->TableImporter_preprocessEngine +TableImporter_preprocessEngine->chunkProcessor: process +chunkProcessor->chunkProcessor:deliverLoop +chunkProcessor->Writer: AppendRows +Writer->tidbBackend: WriteRows +Controller_Run->Controller: cleanCheckpoints + #+END_SRC + + region split logic + #+BEGIN_SRC plantuml :file ./png/region_split.png + participant Backend_ImportEngine + + participant Backend_startWorker [ + =Backend + ---- + ""startWorker"" + ] + participant Backend_executeJob [ + =Backend + ---- + ""executeJob"" + ] + participant Backend_writeToTiKV [ + =Backend + ---- + ""writeToTiKV"" + ] + participant Backend_ingest [ + =Backend + ---- + ""ingest"" + ] + + Backend_ImportEngine->Backend_ImportEngine: externalEngine + Backend_ImportEngine->Backend_ImportEngine:KVStatistics + Backend_ImportEngine->Backend_ImportEngine:GetRegionSplitSizeKeys + Backend_ImportEngine->Backend_ImportEngine:PausePDSchedulerScopeTable + Backend_ImportEngine->readAndSplitIntoRange: Split range + readAndSplitIntoRange->readAndSplitIntoRange: GetKeyRange + readAndSplitIntoRange->Engine_SplitRanges + Engine_SplitRanges->getSizePropertiesFn: Get properties for region split + Engine_SplitRanges->splitRangeBySizeProps: Split region + Backend_ImportEngine->Backend_ImportEngine: SwitchModeByKeyRanges + Backend_ImportEngine->Backend_ImportEngine:doImport + Backend_ImportEngine->Backend_startWorker: start worker to do import + Backend_startWorker->Backend_executeJob: execute job + Backend_executeJob->Backend_executeJob: Check store access and size + Backend_executeJob->Backend_writeToTiKV: Todo need to dig into detail + Backend_executeJob->Backend_ingest: todo + #+END_SRC +* Reference Code +** Run call + #+BEGIN_SRC + opts := []func(context.Context) error{ + rc.setGlobalVariables, + rc.restoreSchema, + rc.preCheckRequirements, + rc.initCheckpoint, + rc.importTables, + rc.fullCompact, + rc.cleanCheckpoints, + } + #+END_SRC +* Questions +** How is the split command sent to PD? + The region split might not through PD. It is calculated from lighting itself. +** Does doImport only generate sst file from pebble? If this is the case, the sst file must be uploaded to TiKV? +** Where is the logic to decide which peer is elected as one sst file? +** What's the relationship between sst file? Is it same on the lighting diff --git a/embed/templates/config/aws-tidb-low.yaml b/embed/templates/config/aws-tidb-low.yaml index eefb7f25..f5cbe68e 100644 --- a/embed/templates/config/aws-tidb-low.yaml +++ b/embed/templates/config/aws-tidb-low.yaml @@ -1,16 +1,27 @@ +workstation: + cidr: 172.82.0.0/16 # The cidr for the VPC + imageid: ami-07d02ee1eeb0c996c # Workstation EC2 instance + keyname: jay-us-east-01 # Public key for workstation instance deployment + keyfile: /home/pi/.ssh/jay-us-east-01.pem # Private key to access the workstation + volumeSize: 100 # disk size in the workstation + enable_monitoring: enabled # enable the moniroting on the workstation + instance_type: t2.medium # Instance type for PD component aws_topo_configs: general: - imageid: ami-0ac97798ccf296e02 # Image ID for TiDB cluster's EC2 node - keyname: jay.pingcap # key name to login from workstation to EC2 nodes - cidr: 172.83.0.0/16 # VPC cidr - instance_type: t2.medium # default instance type for EC2 nodes - tidb_version: v5.2.0 # TiDB version to deploy + imageid: ami-07d02ee1eeb0c996c # Image ID for TiDB cluster's EC2 node + keyname: jay-us-east-01 # Public key for workstation instance deployment + keyfile: /home/pi/.ssh/jay-us-east-01.pem # Private key to access the workstation + cidr: 172.83.0.0/16 # VPC cidr + instance_type: t2.medium # default instance type for EC2 nodes + tidb_version: v7.5.0 # TiDB version to deploy + network_type: nat # network type: nat, private pd: - instance_type: t2.micro # PD instance type - count: 3 # Number of PD nodes to generate + instance_type: t2.micro # PD instance type + count: 3 # Number of PD nodes to generate tidb: - instance_type: t2.medium # TiDB instance type - count: 2 # Number of TiDB nodes to generate + instance_type: t2.medium # TiDB instance type + count: 2 # Number of TiDB nodes to generate tikv: - instance_type: t2.medium # TiKV instance type - count: 3 # Number of TiKV nodes to generate + - + instance_type: t2.medium # TiKV instance type + count: 3 # Number of TiKV nodes to generate diff --git a/embed/templates/config/tidb-cluster.yml b/embed/templates/config/tidb-cluster.yml index 05838714..d91914c0 100644 --- a/embed/templates/config/tidb-cluster.yml +++ b/embed/templates/config/tidb-cluster.yml @@ -9,7 +9,7 @@ server_configs: per-table-memory-quota: 20971520 {{- end }} tidb: - performance.txn-total-size-limit: 107374182400 + performance.txn-total-size-limit: 10737418240 pd: controller.ltb-max-wait-duration: "3600s" {{- if (and (.Labels) (gt (len .Labels) 0)) }} diff --git a/embed/templates/config/tidb-cluster.yml.tpl b/embed/templates/config/tidb-cluster.yml.tpl index 7a07b70e..44297351 100644 --- a/embed/templates/config/tidb-cluster.yml.tpl +++ b/embed/templates/config/tidb-cluster.yml.tpl @@ -9,7 +9,7 @@ server_configs: per-table-memory-quota: 20971520 {{- end }} tidb: - performance.txn-total-size-limit: 107374182400 + performance.txn-total-size-limit: 10737418240 pd: controller.ltb-max-wait-duration: "2h" {{- if (and (.Servers.Labels) (gt (len .Servers.Labels) 0)) }} diff --git a/embed/templates/config/tidb-cluster.yml.tpl.label b/embed/templates/config/tidb-cluster.yml.tpl.label index 4be40dd6..21d9510c 100644 --- a/embed/templates/config/tidb-cluster.yml.tpl.label +++ b/embed/templates/config/tidb-cluster.yml.tpl.label @@ -9,7 +9,7 @@ server_configs: per-table-memory-quota: 20971520 {{- end }} tidb: - performance.txn-total-size-limit: 107374182400 + performance.txn-total-size-limit: 10737418240 {{- if (and (.Labels) (gt (len .Labels) 0)) }} pd: replication.location-labels: [ {{ range .Labels -}} "{{. }}" {{- end }} ] diff --git a/embed/templates/config/tidb-cluster.yml.tpl.nolabel b/embed/templates/config/tidb-cluster.yml.tpl.nolabel index e2f7dfc9..292bee80 100644 --- a/embed/templates/config/tidb-cluster.yml.tpl.nolabel +++ b/embed/templates/config/tidb-cluster.yml.tpl.nolabel @@ -9,7 +9,7 @@ server_configs: per-table-memory-quota: 20971520 {{- end }} tidb: - performance.txn-total-size-limit: 107374182400 + performance.txn-total-size-limit: 10737418240 {{- if (and (.Labels) (gt (len .Labels) 0)) }} pd: replication.location-labels: [ {{ range .Labels -}} "{{. }}" {{- end }} ] diff --git a/pkg/aws/manager/tidb.go b/pkg/aws/manager/tidb.go index 61c6deb2..c2c34c60 100644 --- a/pkg/aws/manager/tidb.go +++ b/pkg/aws/manager/tidb.go @@ -422,6 +422,235 @@ func (m *Manager) TiDBScale( return nil } +// ------------- Placement Rule +func (m *Manager) TiDBPlacementRulePrepareCluster(clusterName, clusterType string, opt operator.LatencyWhenBatchOptions, gOpt operator.Options) error { + // 01. Setup the execution environment + ctx := context.WithValue(context.Background(), "clusterName", clusterName) + ctx = context.WithValue(ctx, "clusterType", clusterType) + + var timer awsutils.ExecutionTimer + timer.Initialize([]string{"Step", "Duration(s)"}) + + if err := m.makeExeContext(ctx, nil, &gOpt, INC_WS, ws.INC_AWS_ENV); err != nil { + return err + } + + if err := m.workstation.InstallPackages(&[]string{"zip", "sysbench"}); err != nil { + return err + } + + if err := m.workstation.InstallToolkit("v7.5.0"); err != nil { + return err + } + + timer.Take("Package Install") + + // 03. Create the necessary tidb resources + var queries []string + if opt.TiKVMode == "partition" { + queries = []string{"drop database if exists latencytest", // Drop the latencytest if not exists(For batch) + fmt.Sprintf("drop database if exists %s", opt.SysbenchDBName), // Drop the sbtest if not exists(fosysbench) + "DROP PLACEMENT POLICY if exists policy_online", // Drop the placement rule + "DROP PLACEMENT POLICY if exists policy_batch", // Drop the placement rule + "CREATE PLACEMENT POLICY policy_online CONSTRAINTS=\"[+db_type=online]\"", // Add the placement policy for online label + "CREATE PLACEMENT POLICY policy_batch CONSTRAINTS=\"[+db_type=batch]\"", // Add the placement policy for batch + "set global tidb_enable_alter_placement=true", + // Todo set one parameter to allow the database creation with placement rule + fmt.Sprintf("create database %s PLACEMENT POLICY=policy_online", opt.SysbenchDBName), // Create the database assigned with online label + "create database latencytest PLACEMENT POLICY=policy_batch", // Create the database assigned with batch label + } + } else { + queries = []string{"drop database if exists latencytest", + "create database latencytest", + fmt.Sprintf("drop database if exists %s", opt.SysbenchDBName), + fmt.Sprintf("create database %s", opt.SysbenchDBName), + } + } + + queries = append(queries, "create user if not exists `batchusr`@`%` identified by \"1234Abcd\"", + "create user if not exists `onlineusr`@`%` identified by \"1234Abcd\"", + "grant all on *.* to `onlineusr`@`%` ", + "grant all on *.* to `batchusr`@`%` ", + ) + + // if opt.IsolationMode == "ResourceControl" { + // queries = append(queries, "create resource group if not exists sg_online ru_per_sec=15000 priority = high burstable", + // "create resource group if not exists sg_batch ru_per_sec=2000 priority = low QUERY_LIMIT=(EXEC_ELAPSED=\"120m\", ACTION=COOLDOWN)", + // "alter user `onlineusr`@`%` resource group sg_online", + // "alter user `batchusr`@`%` resource group sg_batch", + // ) + // } + + for _, query := range queries { + if _, _, err := m.wsExe.Execute(ctx, fmt.Sprintf("/opt/scripts/run_tidb_query mysql '%s'", query), false, 1*time.Hour); err != nil { + return err + } + } + + // 04. Create ontime table to populate test data + if _, _, err := m.wsExe.Execute(ctx, fmt.Sprintf("/opt/scripts/run_tidb_from_file %s '%s'", "latencytest", "/opt/tidb/sql/ontime_tidb.ddl"), false, 1*time.Hour); err != nil { + return err + } + + if _, _, err := m.wsExe.Execute(ctx, fmt.Sprintf("/opt/scripts/run_tidb_query %s '%s'", "latencytest", "create table ontime01 like ontime;"), false, 1*time.Hour); err != nil { + return err + } + + timer.Take("DB Resource preparation") + + // 05. Data preparation from external + for _, file := range []string{"download_import_ontime.sh", "ontime_batch_insert.sh", "ontime_shard_batch_insert.sh"} { + if err := task.TransferToWorkstation(&m.wsExe, fmt.Sprintf("templates/scripts/%s", file), fmt.Sprintf("/opt/scripts/%s", file), "0755", []string{}); err != nil { + return err + } + } + + // 06. Get DB info + dbConnInfo, err := m.workstation.GetTiDBDBInfo() + if err != nil { + return err + } + + // Render dumpling script + tplDumpling := make(map[string]string) + tplDumpling["TiDBHost"] = (*dbConnInfo).DBHost + tplDumpling["TiDBPort"] = strconv.FormatInt(int64((*dbConnInfo).DBPort), 10) // fmt.Sprintf("%s", (*dbConnInfo).DBPort) + tplDumpling["TiDBUser"] = "batchusr" + tplDumpling["TiDBPassword"] = "1234Abcd" + tplDumpling["DBName"] = "latencytest" + if err = task.TransferToWorkstation(&m.wsExe, "templates/scripts/dumpling_data.sh.tpl", "/usr/local/bin/dumpling_data", "0755", tplDumpling); err != nil { + return err + } + + tplSysbenchParam := make(map[string]string) + tplSysbenchParam["TiDBHost"] = (*dbConnInfo).DBHost + tplSysbenchParam["TiDBPort"] = strconv.FormatInt(int64((*dbConnInfo).DBPort), 10) // fmt.Sprintf("%s", (*dbConnInfo).DBPort) + tplSysbenchParam["TiDBUser"] = "onlineusr" + tplSysbenchParam["TiDBPassword"] = "1234Abcd" + tplSysbenchParam["TiDBDBName"] = opt.SysbenchDBName + tplSysbenchParam["ExecutionTime"] = strconv.FormatInt(int64(opt.SysbenchExecutionTime), 10) + tplSysbenchParam["Thread"] = strconv.Itoa(opt.SysbenchThread) + tplSysbenchParam["ReportInterval"] = strconv.Itoa(opt.SysbenchReportInterval) + + // 05. Setup the sysbench + if err = task.TransferToWorkstation(&m.wsExe, "templates/config/sysbench.toml.tpl", "/opt/sysbench.toml", "0644", tplSysbenchParam); err != nil { + return err + } + + if err = task.TransferToWorkstation(&m.wsExe, "templates/config/tidb-lightning.toml.tpl", "/opt/tidb-lightning.toml", "0644", tplSysbenchParam); err != nil { + return err + } + timer.Take("Template render") + + // Truncate table before data import(lightning local) + if err := m.workstation.ExecuteTiDB("latencytest", "truncate table ontime01"); err != nil { + return err + } + + startYM := strings.Split(opt.OnTimeStart, "-") + endYM := strings.Split(opt.OnTimeEnd, "-") + + // Download the data for ontime data population + if _, _, err := m.wsExe.Execute(ctx, fmt.Sprintf("/opt/scripts/download_import_ontime.sh %s %s %s %s %s %s 1>/dev/null", "latencytest", "ontime01", startYM[0], strings.TrimLeft(startYM[1], "0"), endYM[0], strings.TrimLeft(endYM[1], "0")), false, 1*time.Hour); err != nil { + return err + } + + timer.Take("Batch data import(ontime)") + + if _, _, err = m.wsExe.Execute(ctx, fmt.Sprintf("sysbench --config-file=%s %s --tables=%d --table-size=%d prepare", "/opt/sysbench.toml", opt.SysbenchPluginName, opt.SysbenchNumTables, opt.SysbenchNumRows), false, 1*time.Hour); err != nil { + return err + } + + timer.Take("sysbench preparation") + + for _, file := range []string{"tidb_common.lua", "tidb_oltp_insert.lua", "tidb_oltp_point_select.lua", "tidb_oltp_read_write.lua", "tidb_oltp_insert_simple.lua", "tidb_oltp_point_select_simple.lua", "tidb_oltp_read_write_simple.lua", "tidb_bulk_insert.lua"} { + if err = task.TransferToWorkstation(&m.wsExe, fmt.Sprintf("templates/scripts/sysbench/%s", file), fmt.Sprintf("/usr/share/sysbench/%s", file), "0644", []string{}); err != nil { + return err + } + } + + timer.Take("sysbench scripts render ") + timer.Print() + + return nil + +} + +// isolation-mode: +// 01. No +// 02. PlacementRule +func (m *Manager) TiDBPlacementRuleRunCluster(clusterName, clusterType string, opt operator.LatencyWhenBatchOptions, gOpt operator.Options) error { + ctx, cancel := context.WithCancel(context.Background()) + + ctx = context.WithValue(ctx, "clusterName", clusterName) + ctx = context.WithValue(ctx, "clusterType", clusterType) + + err := m.makeExeContext(ctx, nil, &gOpt, INC_WS, ws.EXC_AWS_ENV) + if err != nil { + return err + } + + var sysbenchResult [][]string + var cntInsert int64 + for idx := 0; idx < opt.RunCount; idx++ { + + for idxBatchSize, batchSize := range strings.Split(opt.BatchSizeArray, ",") { + cntInsert = 0 + // 01. Set the context + ctx, cancel = context.WithCancel(context.Background()) + ctx = context.WithValue(ctx, "clusterName", clusterName) + ctx = context.WithValue(ctx, "clusterType", clusterType) + + // 02. Prepare the task + var envInitTasks []*task.StepDisplay // tasks which are used to initialize environment + + t1 := task.NewBuilder().RunSysbench(&m.wsExe, "/opt/sysbench.toml", &sysbenchResult, &opt, &cancel).BuildAsStep(fmt.Sprintf(" - Running Ontime Transaction")) + envInitTasks = append(envInitTasks, t1) + + // 03. If the batch size is not x, run the data batch insert + if batchSize != "x" { + opt.BatchSize, err = strconv.Atoi(batchSize) + if err != nil { + return err + } + + opt.BatchMode = "insert" + t2 := task.NewBuilder().RunOntimeBatchInsert(&m.workstation, &opt, &cntInsert).BuildAsStep(fmt.Sprintf(" - Running Ontime batch")) + envInitTasks = append(envInitTasks, t2) + } else { + opt.BatchSize = 0 + } + + // 04. Run sysbench + builder := task.NewBuilder().ParallelStep(fmt.Sprintf("+ Running %d round %d th test for batch-size: %s", idx+1, idxBatchSize+1, batchSize), false, envInitTasks...) + + t := builder.Build() + + if err := t.Execute(ctxt.New(ctx, 2)); err != nil { + if errorx.Cast(err) != nil { + return err + } + return err + } + + time.Sleep(20 * time.Second) + + lastItem := sysbenchResult[len(sysbenchResult)-1] + + lastItem = append([]string{fmt.Sprintf("%d", cntInsert)}, lastItem...) + lastItem = append([]string{fmt.Sprintf("batchsize: %s", batchSize)}, lastItem...) + + // 008. Fixed the message + sysbenchResult = append(sysbenchResult[:len(sysbenchResult)-1], lastItem) + } + + } + + tui.PrintTable(sysbenchResult, true) + + return nil +} + // ------------- Latency measurement func (m *Manager) TiDBMeasureLatencyPrepareCluster(clusterName, clusterType string, opt operator.LatencyWhenBatchOptions, gOpt operator.Options) error { // 01. Setup the execution environment @@ -576,6 +805,15 @@ func (m *Manager) TiDBMeasureLatencyPrepareCluster(clusterName, clusterType stri } +func (m *Manager) TiDBPlacementRuleCleanupCluster(clusterName, clusterType string, gOpt operator.Options) error { + fmt.Printf("Running in the clean phase ") + fmt.Printf("Remove the database") + return nil +} + +// isolation-mode: +// 01. No +// 02. PlacementRule func (m *Manager) TiDBMeasureLatencyRunCluster(clusterName, clusterType string, opt operator.LatencyWhenBatchOptions, gOpt operator.Options) error { ctx, cancel := context.WithCancel(context.Background()) @@ -587,15 +825,15 @@ func (m *Manager) TiDBMeasureLatencyRunCluster(clusterName, clusterType string, return err } - _data, err := m.workstation.QueryTiDB("test", "calibrate resource") - if err != nil { - return err - } - rcQuota := (*_data)[0]["QUOTA"].(float64) - var sysbenchResult [][]string for idx := 0; idx < opt.RunCount; idx++ { if opt.IsolationMode == "ResourceControl" { + _data, err := m.workstation.QueryTiDB("test", "calibrate resource") + if err != nil { + return err + } + rcQuota := (*_data)[0]["QUOTA"].(float64) + // var arrConfig [][]string arrConfig := [][]string{ // title, rcu @@ -612,13 +850,6 @@ func (m *Manager) TiDBMeasureLatencyRunCluster(clusterName, clusterType string, } for _, _testCase := range arrConfig { - // 001. Count the batch table before batch - // _data, err := m.workstation.QueryTiDB("test", "select count(*) ontime_cnt from latencytest.ontime") - // if err != nil { - // return err - // } - // originalCnt := (*_data)[0]["ontime_cnt"].(float64) - // 002. Change the resource control for batch if _testCase[1] != "" { intCoe, err := strconv.Atoi(_testCase[1]) @@ -663,17 +894,8 @@ func (m *Manager) TiDBMeasureLatencyRunCluster(clusterName, clusterType string, return err } - // 007. Count after execution - // _data, err = m.workstation.QueryTiDB("test", "select count(*) ontime_cnt from latencytest.ontime") - // if err != nil { - // return err - // } - // cnt := (*_data)[0]["ontime_cnt"].(float64) - originalCnt - - // fmt.Printf("Inserted row here: %d vs %f \n\n\n\n\n\n", dumplingCnt, cnt) - lastItem := sysbenchResult[len(sysbenchResult)-1] - // lastItem = append([]string{fmt.Sprintf("%d", int(math.Round(cnt)))}, lastItem...) + lastItem = append([]string{fmt.Sprintf("%d", dumplingCnt)}, lastItem...) lastItem = append([]string{_testCase[0]}, lastItem...) @@ -683,6 +905,8 @@ func (m *Manager) TiDBMeasureLatencyRunCluster(clusterName, clusterType string, time.Sleep(30 * time.Second) } } else { + // Placement rule: + for idxBatchSize, batchSize := range strings.Split(opt.BatchSizeArray, ",") { // 01. Set the context ctx, cancel = context.WithCancel(context.Background()) diff --git a/pkg/aws/manager/workstation.go b/pkg/aws/manager/workstation.go index ede0bcc0..beb40802 100644 --- a/pkg/aws/manager/workstation.go +++ b/pkg/aws/manager/workstation.go @@ -20,7 +20,7 @@ import ( "github.com/fatih/color" "github.com/joomcode/errorx" - "github.com/luyomo/OhMyTiUP/pkg/aws/clusterutil" + // "github.com/luyomo/OhMyTiUP/pkg/aws/clusterutil" operator "github.com/luyomo/OhMyTiUP/pkg/aws/operation" "github.com/luyomo/OhMyTiUP/pkg/aws/spec" "github.com/luyomo/OhMyTiUP/pkg/aws/task" @@ -30,7 +30,7 @@ import ( "github.com/luyomo/OhMyTiUP/pkg/logger" "github.com/luyomo/OhMyTiUP/pkg/meta" - "github.com/luyomo/OhMyTiUP/pkg/set" + // "github.com/luyomo/OhMyTiUP/pkg/set" "github.com/luyomo/OhMyTiUP/pkg/tui" "github.com/luyomo/OhMyTiUP/pkg/utils" perrs "github.com/pingcap/errors" @@ -60,125 +60,62 @@ import ( // Deploy a cluster. func (m *Manager) WorkstationDeploy( - name string, + name, clusterType string, topoFile string, opt DeployOptions, // afterDeploy func(b *task.Builder, newPart spec.Topology), skipConfirm bool, gOpt operator.Options, ) error { - // 1. Preparation phase + + // 01. Preparation phase var timer awsutils.ExecutionTimer timer.Initialize([]string{"Step", "Duration(s)"}) - if err := clusterutil.ValidateClusterNameOrError(name); err != nil { - return err - } - - exist, err := m.specManager.Exist(name) - if err != nil { - return err - } - - if exist { - // FIXME: When change to use args, the suggestion text need to be updatem. - return errDeployNameDuplicate. - New("Cluster name '%s' is duplicated", name). - WithProperty(tui.SuggestionFromFormat("Please specify another cluster name")) - } - + // 02. Get the topo file and parse it metadata := m.specManager.NewMetadata() topo := metadata.GetTopology() - if err := spec.ParseTopologyYaml(topoFile, topo); err != nil { - return err - } - - // spec.ExpandRelativeDir(topo) - + // 03. Setup the ssh type base := topo.BaseTopo() - // fmt.Printf("The config is <%#v> \n\n\n", base.AwsTopoConfigs.TiKV) - // fmt.Printf("All the labels is <%#v> \n\n\n", base.AwsTopoConfigs.TiKV.Labels) - // for _, label := range base.AwsTopoConfigs.TiKV.Labels { - // fmt.Printf("The label is <%#v> \n\n\n", label) - // for _, nodeLabel := range label.Values { - // fmt.Printf("The node label is <%#v> \n\n\n", nodeLabel) - // } - // } - // fmt.Printf("All the modal type is <%#v> \n\n\n", base.AwsTopoConfigs.TiKV.ModalTypes) - // return nil if sshType := gOpt.SSHType; sshType != "" { base.GlobalOptions.SSHType = sshType } - // var ( - // sshConnProps *tui.SSHConnectionProps = &tui.SSHConnectionProps{} - // sshProxyProps *tui.SSHConnectionProps = &tui.SSHConnectionProps{} - // ) - // if gOpt.SSHType != executor.SSHTypeNone { - // var err error - // if sshConnProps, err = tui.ReadIdentityFileOrPassword(opt.IdentityFile, opt.UsePassword); err != nil { - // return err - // } - // if len(gOpt.SSHProxyHost) != 0 { - // if sshProxyProps, err = tui.ReadIdentityFileOrPassword(gOpt.SSHProxyIdentity, gOpt.SSHProxyUsePassword); err != nil { - // return err - // } - // } - // } - - // if err := m.fillHostArch(sshConnProps, sshProxyProps, topo, &gOpt, opt.User); err != nil { - // return err - // } - - if !skipConfirm { - if err := m.confirmTopology(name, "v5.1.0", topo, set.NewStringSet()); err != nil { - return err - } - } - - // if err := os.MkdirAll(m.specManager.Path(name), 0755); err != nil { - // return errorx.InitializationFailed. - // Wrap(err, "Failed to create cluster metadata directory '%s'", m.specManager.Path(name)). - // WithProperty(tui.SuggestionFromString("Please check file system permissions and try again.")) - // } - - var envInitTasks []*task.StepDisplay // tasks which are used to initialize environment - - // globalOptions := base.GlobalOptions - - sexecutor, err := executor.New(executor.SSHTypeNone, false, executor.SSHConfig{Host: "127.0.0.1", User: utils.CurrentUser()}, []string{}) - if err != nil { + // 04. Confirm the topo config + if err := spec.ParseTopologyYaml(topoFile, topo); err != nil { return err } - clusterType := "ohmytiup-workstation" ctx := context.WithValue(context.Background(), "clusterName", name) ctx = context.WithValue(ctx, "clusterType", clusterType) - ctx = context.WithValue(ctx, "tagOwner", gOpt.TagOwner) - ctx = context.WithValue(ctx, "tagProject", gOpt.TagProject) + + if err := m.makeExeContext(ctx, nil, &gOpt, EXC_WS, ws.EXC_AWS_ENV); err != nil { + return err + } var workstationInfo task.ClusterInfo - if base.AwsWSConfigs.InstanceType == "" { - return errors.New("No workstation instance is specified") - } + var task001 []*task.StepDisplay // tasks which are used to initialize environment fpMakeWSContext := func() error { if err := m.makeExeContext(ctx, nil, &gOpt, INC_WS, ws.EXC_AWS_ENV); err != nil { return err } + return nil } - t1 := task.NewBuilder().CreateWorkstationCluster(&sexecutor, "workstation", base.AwsWSConfigs, &workstationInfo, &m.wsExe, &gOpt, fpMakeWSContext). + t1 := task.NewBuilder(). + CreateWorkstationCluster(&m.localExe, "workstation", base.AwsWSConfigs, &workstationInfo, &m.wsExe, &gOpt, fpMakeWSContext). BuildAsStep(fmt.Sprintf(" - Preparing workstation")) - envInitTasks = append(envInitTasks, t1) + task001 = append(task001, t1) - builder := task.NewBuilder().ParallelStep("+ Deploying workstation ... ...", false, envInitTasks...) - - t := builder.Build() + paraTask001 := task.NewBuilder(). + CreateTransitGateway(&m.localExe). + ParallelStep("+ Deploying all the sub components", false, task001...). + BuildAsStep("Parallel Main step") - if err := t.Execute(ctxt.New(ctx, gOpt.Concurrency)); err != nil { + if err := paraTask001.Execute(ctxt.New(ctx, 10)); err != nil { if errorx.Cast(err) != nil { // FIXME: Map possible task errors and give suggestions. return err @@ -193,10 +130,101 @@ func (m *Manager) WorkstationDeploy( logger.OutputDebugLog("aws-nodes") return nil + // -------------------------- + + // // 1. Preparation phase + // var timer awsutils.ExecutionTimer + // timer.Initialize([]string{"Step", "Duration(s)"}) + + // if err := clusterutil.ValidateClusterNameOrError(name); err != nil { + // return err + // } + + // exist, err := m.specManager.Exist(name) + // if err != nil { + // return err + // } + + // if exist { + // // FIXME: When change to use args, the suggestion text need to be updatem. + // return errDeployNameDuplicate. + // New("Cluster name '%s' is duplicated", name). + // WithProperty(tui.SuggestionFromFormat("Please specify another cluster name")) + // } + + // metadata := m.specManager.NewMetadata() + // topo := metadata.GetTopology() + + // if err := spec.ParseTopologyYaml(topoFile, topo); err != nil { + // return err + // } + + // base := topo.BaseTopo() + + // if sshType := gOpt.SSHType; sshType != "" { + // base.GlobalOptions.SSHType = sshType + // } + + // if !skipConfirm { + // if err := m.confirmTopology(name, "v5.1.0", topo, set.NewStringSet()); err != nil { + // return err + // } + // } + + // var envInitTasks []*task.StepDisplay // tasks which are used to initialize environment + + // // globalOptions := base.GlobalOptions + + // sexecutor, err := executor.New(executor.SSHTypeNone, false, executor.SSHConfig{Host: "127.0.0.1", User: utils.CurrentUser()}, []string{}) + // if err != nil { + // return err + // } + // clusterType := "ohmytiup-workstation" + + // ctx := context.WithValue(context.Background(), "clusterName", name) + // ctx = context.WithValue(ctx, "clusterType", clusterType) + // ctx = context.WithValue(ctx, "tagOwner", gOpt.TagOwner) + // ctx = context.WithValue(ctx, "tagProject", gOpt.TagProject) + + // var workstationInfo task.ClusterInfo + + // if base.AwsWSConfigs.InstanceType == "" { + // return errors.New("No workstation instance is specified") + // } + + // fpMakeWSContext := func() error { + // if err := m.makeExeContext(ctx, nil, &gOpt, INC_WS, ws.EXC_AWS_ENV); err != nil { + // return err + // } + // return nil + // } + // t1 := task.NewBuilder().CreateWorkstationCluster(&sexecutor, "workstation", base.AwsWSConfigs, &workstationInfo, &m.wsExe, &gOpt, fpMakeWSContext). + // BuildAsStep(fmt.Sprintf(" - Preparing workstation")) + // envInitTasks = append(envInitTasks, t1) + + // builder := task.NewBuilder().ParallelStep("+ Deploying workstation ... ...", false, envInitTasks...) + + // t := builder.Build() + + // if err := t.Execute(ctxt.New(ctx, gOpt.Concurrency)); err != nil { + // if errorx.Cast(err) != nil { + // // FIXME: Map possible task errors and give suggestions. + // return err + // } + // return err + // } + + // timer.Take("Execution") + + // // 8. Print the execution summary + // timer.Print() + + // logger.OutputDebugLog("aws-nodes") + // return nil } // DestroyCluster destroy the cluster. -func (m *Manager) DestroyWorkstation(name string, gOpt operator.Options, skipConfirm bool) error { +func (m *Manager) DestroyWorkstation(name, clusterType string, gOpt operator.Options, skipConfirm bool) error { _, err := m.meta(name) if err != nil && !errors.Is(perrs.Cause(err), meta.ErrValidate) && !errors.Is(perrs.Cause(err), spec.ErrNoTiSparkMaster) && @@ -205,7 +233,7 @@ func (m *Manager) DestroyWorkstation(name string, gOpt operator.Options, skipCon return err } - clusterType := "ohmytiup-workstation" + // clusterType := "ohmytiup-workstation" sexecutor, err := executor.New(executor.SSHTypeNone, false, executor.SSHConfig{Host: "127.0.0.1", User: utils.CurrentUser()}, []string{}) if err != nil { @@ -241,11 +269,11 @@ func (m *Manager) DestroyWorkstation(name string, gOpt operator.Options, skipCon // Cluster represents a clsuter // ListCluster list the clusters. -func (m *Manager) ListWorkstation(clusterName string, opt DeployOptions) error { +func (m *Manager) ListWorkstation(clusterName, clusterType string, opt DeployOptions) error { var listTasks []*task.StepDisplay // tasks which are used to initialize environment - clusterType := "ohmytiup-workstation" + // clusterType := "ohmytiup-workstation" ctx := context.WithValue(context.Background(), "clusterName", clusterName) ctx = context.WithValue(ctx, "clusterType", clusterType) diff --git a/terraform/tidb-on-aks/masterdb/main.tf b/terraform/tidb-on-aks/masterdb/main.tf index c0cd3211..c1afac59 100644 --- a/terraform/tidb-on-aks/masterdb/main.tf +++ b/terraform/tidb-on-aks/masterdb/main.tf @@ -8,7 +8,7 @@ module "masteraks" { k8s_version = "1.25.11" ticdc_node_count = 3 kube_config_file = "/tmp/master_kubeconfig" - cluster_version = "v7.1.1" + cluster_version = "v8.0.0" register_app_client_id = "${var.register_app_client_id}" register_app_client_secret = "${var.register_app_client_secret}"