├── .DS_Store ├── .gitignore ├── .vscode └── launch.json ├── Design-patterns-for-asynchronous-API-communication.md ├── LICENSE ├── MISTAKE.md ├── README.md ├── README_VI.md ├── WIREMOCK.md ├── aws ├── .DS_Store ├── apigw │ ├── README.md │ └── mtls │ │ ├── RootCA.key │ │ ├── RootCA.pem │ │ ├── my_client.csr │ │ ├── my_client.key │ │ └── my_client.pem ├── cloudfront-func │ ├── README.md │ └── kvs-hmac-sha256 │ │ ├── README.md │ │ ├── signature.js │ │ └── test-objects │ │ └── valid-hmac.json ├── cloudwatch │ ├── README.md │ └── logGroup │ │ ├── README.md │ │ └── main.go ├── codeartifact │ └── README.md ├── codecommit │ └── README.md ├── lambda │ ├── .DS_Store │ ├── CRUD_POSTGRES.md │ ├── README.md │ ├── courses │ │ └── sharing │ │ │ ├── .gitignore │ │ │ ├── Makefile │ │ │ ├── hello │ │ │ └── main.go │ │ │ ├── serverless.yml │ │ │ └── world │ │ │ └── main.go │ ├── crud │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.MD │ │ ├── create │ │ │ └── main.go │ │ ├── delete │ │ │ └── main.go │ │ ├── models │ │ │ └── user.go │ │ ├── pkg │ │ │ └── db.go │ │ ├── read │ │ │ └── main.go │ │ ├── serverless.yml │ │ ├── sql.sql │ │ └── update │ │ │ └── main.go │ ├── golang-migration-go1.x-provided.al2 │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.md │ │ ├── bootstrap │ │ ├── migration │ │ │ └── main.go │ │ └── serverless.yml │ ├── hello-api │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.md │ │ ├── hello │ │ │ └── main.go │ │ └── serverless.yml │ ├── images │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── functions │ │ │ ├── authorizer │ │ │ │ ├── authorizer.yml │ │ │ │ └── openapi-lambda-auth.py │ │ │ ├── getImage │ │ │ │ ├── main.go │ │ │ │ └── validator.json │ │ │ └── insertImage │ │ │ │ ├── main.go │ │ │ │ └── validator.json │ │ └── serverless.yml │ ├── lambda-go-1-2 │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.MD │ │ ├── hello │ │ │ └── main.go │ │ ├── serverless.yml │ │ └── world │ │ │ └── main.go │ └── lambda-go-3 │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.MD │ │ ├── create │ │ └── main.go │ │ ├── delete │ │ └── main.go │ │ ├── models │ │ └── user.go │ │ ├── pkg │ │ └── db.go │ │ ├── read │ │ └── main.go │ │ ├── serverless.yml │ │ ├── sql.sql │ │ └── update │ │ └── main.go ├── s3 │ ├── README.md │ ├── delete.go │ ├── error.html │ ├── image │ │ ├── handler-upload.png │ │ ├── upload-success-s3.png │ │ └── upload.png │ ├── index.html │ ├── s3.go │ └── upload.go ├── secretsmanager │ └── README.md ├── sqs │ ├── README.md │ ├── README_VI.md │ ├── lambda │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.MD │ │ ├── push │ │ │ └── main.go │ │ ├── receive │ │ │ └── main.go │ │ └── serverless.yml │ ├── main.go │ └── sqs.go └── textract │ └── main.go ├── concurrency ├── README.md ├── errorHandling.go ├── fanInfanOut.go ├── leak.go ├── patterns │ ├── README.md │ ├── fanInfanOut │ │ └── main.go │ ├── pipeline │ │ └── main.go │ ├── pubSub │ │ └── main.go │ ├── rateLimit │ │ └── main.go │ ├── selectTimeout │ │ └── main.go │ ├── semaphore │ │ └── main.go │ └── workerpool │ │ └── main.go ├── preventLeak.go ├── selectStatement.go ├── singleflight │ ├── main.go │ └── output.png └── syncCond.go ├── database └── index │ ├── README.md │ └── note.txt ├── dtm ├── .gitignore ├── dtm-cases │ ├── .gitignore │ ├── LICENSE │ ├── README-cn.md │ ├── README.md │ ├── cache │ │ ├── README-cn.md │ │ ├── README.md │ │ ├── demo │ │ │ ├── api-atomic.go │ │ │ ├── api-downgrade.go │ │ │ ├── api-shared.go │ │ │ ├── api-strong.go │ │ │ ├── api-version.go │ │ │ ├── data.go │ │ │ └── main.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ └── table.sql │ ├── flash │ │ ├── README-cn.md │ │ ├── README.md │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── order │ │ ├── README-cn.md │ │ ├── README.md │ │ ├── common │ │ │ └── common.go │ │ ├── conf │ │ │ └── conf.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ ├── order.sql │ │ └── service │ │ │ ├── api.go │ │ │ ├── coupon.go │ │ │ ├── order.go │ │ │ ├── pay.go │ │ │ ├── stock.go │ │ │ └── types.go │ └── utils │ │ ├── go.mod │ │ ├── go.sum │ │ └── utils.go ├── dtm-examples │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── busi │ │ ├── barrier.go │ │ ├── base_grpc.go │ │ ├── base_http.go │ │ ├── base_jrpc.go │ │ ├── base_types.go │ │ ├── busi.pb.go │ │ ├── busi.proto │ │ ├── busi_grpc.pb.go │ │ ├── data.go │ │ ├── quick_start.go │ │ ├── startup.go │ │ └── utils.go │ ├── doc │ │ ├── README-cn.md │ │ └── README-en.md │ ├── dtmutil │ │ ├── consts.go │ │ ├── db.go │ │ └── utils.go │ ├── examples │ │ ├── grpc_headers.go │ │ ├── grpc_msg.go │ │ ├── grpc_saga.go │ │ ├── grpc_saga_barrier.go │ │ ├── grpc_saga_other.go │ │ ├── grpc_tcc.go │ │ ├── grpc_workflow_mixed.go │ │ ├── grpc_workflow_saga.go │ │ ├── grpc_workflow_tcc.go │ │ ├── grpc_xa.go │ │ ├── http_barrier_redis.go │ │ ├── http_headers.go │ │ ├── http_more.go │ │ ├── http_msg.go │ │ ├── http_saga.go │ │ ├── http_saga_barrier.go │ │ ├── http_saga_gorm_barrier.go │ │ ├── http_saga_mongo.go │ │ ├── http_saga_mutidb.go │ │ ├── http_saga_redis.go │ │ ├── http_tcc.go │ │ ├── http_tcc_barrier.go │ │ ├── http_workflow_saga.go │ │ ├── http_workflow_tcc.go │ │ ├── http_workflow_xa.go │ │ ├── http_xa.go │ │ ├── http_xa_gorm.go │ │ ├── startup.go │ │ └── utils.go │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── sync-from-dtm.sh ├── dtm-lab │ ├── LICENSE │ ├── README.md │ ├── dtmcli │ │ ├── barrier.go │ │ ├── barrier_mongo.go │ │ ├── barrier_redis.go │ │ ├── consts.go │ │ ├── dtmimp │ │ │ ├── README-cn.md │ │ │ ├── README.md │ │ │ ├── consts.go │ │ │ ├── db_special.go │ │ │ ├── trans_base.go │ │ │ ├── trans_xa_base.go │ │ │ ├── types.go │ │ │ ├── utils.go │ │ │ └── vars.go │ │ ├── logger │ │ │ └── logger.go │ │ ├── trans_msg.go │ │ ├── trans_saga.go │ │ ├── trans_tcc.go │ │ ├── types.go │ │ ├── utils.go │ │ └── xa.go │ ├── dtmgrpc │ │ ├── barrier.go │ │ ├── dtmgimp │ │ │ ├── README-cn.md │ │ │ ├── README.md │ │ │ ├── grpc_clients.go │ │ │ ├── types.go │ │ │ └── utils.go │ │ ├── dtmgpb │ │ │ ├── dtmgimp.pb.go │ │ │ ├── dtmgimp.proto │ │ │ └── dtmgimp_grpc.pb.go │ │ ├── msg.go │ │ ├── options.go │ │ ├── saga.go │ │ ├── tcc.go │ │ ├── type.go │ │ └── xa.go │ ├── dummy.go │ ├── go.mod │ ├── go.sum │ └── workflow │ │ ├── dummyReadCloser.go │ │ ├── factory.go │ │ ├── imp.go │ │ ├── rpc.go │ │ ├── server.go │ │ ├── utils.go │ │ ├── wfpb │ │ ├── wf.pb.go │ │ ├── wf.proto │ │ └── wf_grpc.pb.go │ │ └── workflow.go ├── dtm │ ├── .github │ │ └── workflows │ │ │ ├── codeql-analysis.yml │ │ │ ├── docker.yml │ │ │ ├── release.yml │ │ │ └── tests.yml │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── admin │ │ ├── .env │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── public │ │ │ └── favicon.ico │ │ ├── src │ │ │ ├── App.vue │ │ │ ├── api │ │ │ │ └── api_dtm.ts │ │ │ ├── assets │ │ │ │ └── css │ │ │ │ │ └── index.css │ │ │ ├── components.d.ts │ │ │ ├── components │ │ │ │ ├── Screenfull │ │ │ │ │ └── index.vue │ │ │ │ └── SvgIcon │ │ │ │ │ └── index.vue │ │ │ ├── icons │ │ │ │ ├── readme.md │ │ │ │ └── svg │ │ │ │ │ ├── fullscreen.svg │ │ │ │ │ └── logo.svg │ │ │ ├── layout │ │ │ │ ├── aside.vue │ │ │ │ ├── components │ │ │ │ │ ├── content.vue │ │ │ │ │ ├── header.vue │ │ │ │ │ └── sidebar.vue │ │ │ │ └── index.vue │ │ │ ├── main.ts │ │ │ ├── permission.ts │ │ │ ├── router │ │ │ │ ├── asyncRouter.ts │ │ │ │ └── index.ts │ │ │ ├── store │ │ │ │ ├── index.ts │ │ │ │ └── modules │ │ │ │ │ └── layout.ts │ │ │ ├── type │ │ │ │ ├── index.d.ts │ │ │ │ ├── shim.vue.d.ts │ │ │ │ └── store │ │ │ │ │ └── layout.ts │ │ │ ├── utils │ │ │ │ ├── request.ts │ │ │ │ └── util.ts │ │ │ └── views │ │ │ │ └── Dashboard │ │ │ │ ├── GlobalTransactions │ │ │ │ ├── AllTransactions.vue │ │ │ │ ├── DialogTransactionDetail.vue │ │ │ │ └── UnfinishedTransactions.vue │ │ │ │ ├── KVPairs │ │ │ │ ├── Topics.vue │ │ │ │ └── _Components │ │ │ │ │ ├── DialogTopicDetail.vue │ │ │ │ │ └── DialogTopicSubscribe.vue │ │ │ │ └── Nodes │ │ │ │ └── LivingNodes.vue │ │ ├── tailwind.config.js │ │ ├── tsconfig.json │ │ ├── vite.config.ts │ │ └── yarn.lock │ ├── charts │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── README.md │ │ ├── templates │ │ │ ├── NOTES.txt │ │ │ ├── _helpers.tpl │ │ │ ├── configmap.yaml │ │ │ ├── deployment.yaml │ │ │ ├── hpa.yaml │ │ │ ├── ingress.yaml │ │ │ ├── service.yaml │ │ │ └── tests │ │ │ │ └── test-connection.yaml │ │ └── values.yaml │ ├── client │ │ ├── README.md │ │ ├── dtmcli │ │ │ ├── barrier.go │ │ │ ├── barrier_mongo.go │ │ │ ├── barrier_redis.go │ │ │ ├── consts.go │ │ │ ├── cover_test.go │ │ │ ├── dtmimp │ │ │ │ ├── README-cn.md │ │ │ │ ├── README.md │ │ │ │ ├── consts.go │ │ │ │ ├── db_special.go │ │ │ │ ├── db_special_test.go │ │ │ │ ├── trans_base.go │ │ │ │ ├── trans_xa_base.go │ │ │ │ ├── types.go │ │ │ │ ├── types_test.go │ │ │ │ ├── utils.go │ │ │ │ ├── utils_test.go │ │ │ │ └── vars.go │ │ │ ├── logger │ │ │ │ └── logger.go │ │ │ ├── trans_msg.go │ │ │ ├── trans_saga.go │ │ │ ├── trans_tcc.go │ │ │ ├── types.go │ │ │ ├── types_test.go │ │ │ ├── utils.go │ │ │ └── xa.go │ │ ├── dtmgrpc │ │ │ ├── barrier.go │ │ │ ├── dtmgimp │ │ │ │ ├── README-cn.md │ │ │ │ ├── README.md │ │ │ │ ├── grpc_clients.go │ │ │ │ ├── types.go │ │ │ │ └── utils.go │ │ │ ├── dtmgpb │ │ │ │ ├── dtmgimp.pb.go │ │ │ │ ├── dtmgimp.proto │ │ │ │ └── dtmgimp_grpc.pb.go │ │ │ ├── msg.go │ │ │ ├── options.go │ │ │ ├── options_test.go │ │ │ ├── saga.go │ │ │ ├── tcc.go │ │ │ ├── type.go │ │ │ ├── type_test.go │ │ │ └── xa.go │ │ └── workflow │ │ │ ├── dummyReadCloser.go │ │ │ ├── factory.go │ │ │ ├── imp.go │ │ │ ├── rpc.go │ │ │ ├── server.go │ │ │ ├── utils.go │ │ │ ├── wfpb │ │ │ ├── wf.pb.go │ │ │ ├── wf.proto │ │ │ └── wf_grpc.pb.go │ │ │ ├── workflow.go │ │ │ └── workflow_test.go │ ├── conf.sample.yml │ ├── dtmsvr │ │ ├── api.go │ │ ├── api_grpc.go │ │ ├── api_http.go │ │ ├── api_json_rpc.go │ │ ├── config │ │ │ ├── config.go │ │ │ ├── config_test.go │ │ │ └── config_utils.go │ │ ├── cron.go │ │ ├── entry │ │ │ └── main.go │ │ ├── metrics.go │ │ ├── microservices │ │ │ └── drivers.go │ │ ├── storage │ │ │ ├── boltdb │ │ │ │ ├── boltdb.go │ │ │ │ └── boltdb_test.go │ │ │ ├── redis │ │ │ │ └── redis.go │ │ │ ├── registry │ │ │ │ ├── factory.go │ │ │ │ └── registry.go │ │ │ ├── sql │ │ │ │ └── sql.go │ │ │ ├── store.go │ │ │ └── trans.go │ │ ├── svr.go │ │ ├── topics.go │ │ ├── trans_class.go │ │ ├── trans_process.go │ │ ├── trans_status.go │ │ ├── trans_type_msg.go │ │ ├── trans_type_saga.go │ │ ├── trans_type_tcc.go │ │ ├── trans_type_workflow.go │ │ ├── trans_type_xa.go │ │ ├── utils.go │ │ └── utils_test.go │ ├── dtmutil │ │ ├── consts.go │ │ ├── db.go │ │ ├── utils.go │ │ └── utils_test.go │ ├── go.mod │ ├── go.sum │ ├── helper │ │ ├── .goreleaser.yml │ │ ├── Dockerfile-release │ │ ├── README-cn.md │ │ ├── README-en.md │ │ ├── bench │ │ │ ├── Makefile │ │ │ ├── main.go │ │ │ ├── prepare.sh │ │ │ ├── setup-redis6.sh │ │ │ ├── setup.sh │ │ │ ├── svr │ │ │ │ └── http.go │ │ │ ├── test-boltdb.sh │ │ │ ├── test-flash-sales.sh │ │ │ ├── test-mysql.sh │ │ │ └── test-redis.sh │ │ ├── compose.store.yml │ │ ├── golint.sh │ │ ├── sync-client.sh │ │ └── test-cover.sh │ ├── main.go │ ├── qs │ │ └── main.go │ ├── revive.toml │ ├── sqls │ │ ├── busi.mongo.js │ │ ├── busi.mysql.sql │ │ ├── busi.postgres.sql │ │ ├── dtmcli.barrier.mongo.js │ │ ├── dtmcli.barrier.mysql.sql │ │ ├── dtmcli.barrier.postgres.sql │ │ ├── dtmsvr.storage.mysql.sql │ │ ├── dtmsvr.storage.postgres.sql │ │ ├── dtmsvr.storage.sqlserver.sql │ │ └── dtmsvr.storage.tdsql.sql │ └── test │ │ ├── api_test.go │ │ ├── base_test.go │ │ ├── busi │ │ ├── barrier.go │ │ ├── base_grpc.go │ │ ├── base_http.go │ │ ├── base_jrpc.go │ │ ├── base_types.go │ │ ├── busi.pb.go │ │ ├── busi.proto │ │ ├── busi_grpc.pb.go │ │ ├── data.go │ │ ├── quick_start.go │ │ ├── startup.go │ │ └── utils.go │ │ ├── common_test.go │ │ ├── dtmsvr_test.go │ │ ├── main_test.go │ │ ├── msg_barrier_mongo_test.go │ │ ├── msg_barrier_redis_test.go │ │ ├── msg_barrier_test.go │ │ ├── msg_delay_test.go │ │ ├── msg_grpc_barrier_redis_test.go │ │ ├── msg_grpc_barrier_test.go │ │ ├── msg_grpc_test.go │ │ ├── msg_jrpc_test.go │ │ ├── msg_options_test.go │ │ ├── msg_test.go │ │ ├── msg_webhook_test.go │ │ ├── saga_barrier_mongo_test.go │ │ ├── saga_barrier_redis_test.go │ │ ├── saga_barrier_test.go │ │ ├── saga_compatible_test.go │ │ ├── saga_concurrent_test.go │ │ ├── saga_grpc_barrier_test.go │ │ ├── saga_grpc_test.go │ │ ├── saga_options_test.go │ │ ├── saga_test.go │ │ ├── store_test.go │ │ ├── tcc_barrier_test.go │ │ ├── tcc_cover_test.go │ │ ├── tcc_grpc_cover_test.go │ │ ├── tcc_grpc_test.go │ │ ├── tcc_jrpc_test.go │ │ ├── tcc_old_test.go │ │ ├── tcc_test.go │ │ ├── topic_test.go │ │ ├── types.go │ │ ├── workflow_base_test.go │ │ ├── workflow_grpc_test.go │ │ ├── workflow_http_ret_test.go │ │ ├── workflow_http_test.go │ │ ├── workflow_interceptor_test.go │ │ ├── workflow_ongoing_test.go │ │ ├── workflow_xa_test.go │ │ ├── xa_cover_test.go │ │ ├── xa_grpc_test.go │ │ └── xa_test.go ├── quick-start-sample │ ├── .gitignore │ ├── LICENSE │ ├── README-cn.md │ ├── README.md │ ├── busi │ │ ├── busi.pb.go │ │ ├── busi.proto │ │ ├── busi_grpc.pb.go │ │ └── busi_server.go │ ├── dtmcli-qs │ │ ├── README-cn.md │ │ ├── README.md │ │ └── main.go │ ├── dtmgrpc-qs │ │ ├── README-cn.md │ │ ├── README.md │ │ └── main.go │ ├── dummy.go │ ├── go.mod │ ├── go.sum │ ├── workflow-grpc │ │ ├── README-cn.md │ │ ├── README.md │ │ └── main.go │ └── workflow-http │ │ ├── README-cn.md │ │ ├── README.md │ │ └── main.go └── sql_dtm.sql ├── elsticsearch ├── README.md ├── main.go └── query │ ├── add.go │ └── search.go ├── gin-web-framework ├── README.md ├── apiExample │ └── index.go └── main.go ├── go.mod ├── go.sum ├── http ├── keep-alive │ ├── README.md │ ├── client │ │ └── client.go │ └── server.go ├── request │ └── http.go ├── retry │ ├── README.md │ └── main.go └── reuse-http │ └── main.go ├── interview └── devops │ ├── DevOps__1718418692.pdf │ └── README.md ├── kafka ├── .DS_Store ├── README.md ├── README_VI.md ├── consumer-rebalance │ ├── README_VI.md │ ├── consumer.md │ ├── consumer │ │ └── main.go │ └── producer │ │ └── main.go ├── consumer │ └── main.go ├── images │ ├── at_least_once_delivery.png │ ├── at_most_once_delivery.png │ ├── consumer_group.png │ ├── segment.png │ ├── segment1.png │ ├── segment2.png │ └── segment3.png ├── mskaws │ ├── README.md │ └── main.go └── producer │ └── main.go ├── letgo ├── .DS_Store ├── README.md ├── chapter2.3 │ ├── README.md │ ├── code.png │ ├── main.go │ ├── readme.png │ └── web.png ├── chapter2.4 │ ├── README.md │ ├── create_user.png │ ├── list_user.png │ ├── main.go │ └── page_not_found.png └── chapter2.9 │ ├── README.md │ ├── code.png │ ├── css_code.png │ ├── folder.png │ ├── main.go │ ├── static_file.png │ └── ui │ ├── efs.go │ ├── html │ ├── base.tmpl │ ├── pages │ │ ├── create.tmpl │ │ ├── home.tmpl │ │ ├── login.tmpl │ │ ├── signup.tmpl │ │ └── view.tmpl │ └── partials │ │ └── nav.tmpl │ └── static │ ├── css │ └── main.css │ ├── img │ ├── favicon.ico │ └── logo.png │ └── js │ └── main.js ├── main.go ├── performances ├── README.MD └── standard │ ├── condition_test.go │ ├── convert.go │ ├── convert_test.go │ └── pointer_test.go ├── redis ├── README.md ├── cluster.yaml ├── cluster │ ├── 7000 │ │ └── redis.conf │ ├── 7001 │ │ └── redis.conf │ ├── 7002 │ │ └── redis.conf │ ├── 7003 │ │ └── redis.conf │ ├── 7004 │ │ └── redis.conf │ ├── 7005 │ │ └── redis.conf │ ├── README.md │ └── docker-compose.yml ├── custom-values.yaml ├── library.yaml ├── main.go ├── master_replica.yaml ├── network.yaml ├── pipeline │ ├── README.md │ ├── README_VI.md │ ├── range.go │ └── range_test.go ├── pubsub │ ├── README.md │ ├── pub │ │ └── main.go │ └── sub │ │ └── main.go ├── queue │ ├── consumer │ │ └── main.go │ └── producer │ │ └── main.go ├── redisPkg │ └── connect.go ├── single.yaml ├── test.json ├── test.txt └── test.yaml ├── rewinding_2024.md ├── structure └── structure.md ├── terraform ├── README.md └── apps │ ├── .gitignore │ ├── apigw │ ├── README.md │ ├── apikey.tf │ ├── deployment.tf │ ├── integration.tf │ ├── main.tf │ ├── method.tf │ ├── resource.tf │ ├── restapi.tf │ ├── stage.tf │ ├── terraform.tfstate │ ├── terraform.tfstate.backup │ ├── usageplan.tf │ └── variables.tf │ ├── apigw_thien │ ├── apikey.tf │ ├── integration.tf │ ├── main.tf │ ├── method.tf │ ├── resource.tf │ ├── usageplan.tf │ └── variables.tf │ ├── lambda │ └── main.tf │ ├── sqs │ ├── main.tf │ ├── queuefifo.tf │ └── variables.tf │ ├── ssm │ ├── main.tf │ ├── paramter.tf │ └── variables.tf │ └── vpc │ ├── main.tf │ ├── subnet.tf │ ├── terraform.tfstate │ ├── terraform.tfstate.backup │ ├── variables.tf │ └── vpc.tf ├── tls └── base64parse │ ├── README.md │ ├── certificate_base64.txt │ ├── file.key │ ├── file.pem │ └── main.go ├── tranning └── README.md └── usecase ├── README.md ├── automatic └── main.go ├── blake ├── 2b.go ├── 2b_test.go ├── 2s.go ├── 2s_test.go └── main.go ├── context ├── README.md ├── cancel.go ├── deadline.go ├── package.go └── timeout.go ├── crypto-demo ├── README.md ├── blake │ ├── 2b.go │ └── 2s.go ├── main.go ├── main_test.go ├── md5 │ └── md5.go └── sha │ └── sha1.go ├── defer └── main.go ├── ecdsa └── ecdsa.go ├── environment ├── README.MD ├── dev.yaml ├── envyaml.go └── prod.yaml ├── file ├── main.go └── s3 │ └── main.go ├── function ├── README.md └── variadic.go ├── images └── resizeImage.png ├── json ├── README.md ├── main.go └── output.json ├── jwttoken ├── README.md └── parse.go ├── loadbalancer └── main.go ├── map ├── .DS_Store ├── p1 │ ├── main.go │ ├── main_test.go │ ├── map_1.1.png │ └── map_1.2.png ├── p2 │ ├── main.go │ ├── main_test.go │ ├── map_2.1.png │ ├── map_2.2.png │ └── map_2.3.png ├── p3 │ ├── main.go │ ├── main_test.go │ ├── map_3.1.png │ ├── map_3.2.png │ ├── map_3.3.png │ └── map_3.4.png └── p4 │ ├── golang_map_4.1.png │ ├── golang_map_4.2.png │ ├── golang_map_4.3.png │ └── main.go ├── password └── main.go ├── resizeImage.go ├── rsa ├── README.md ├── REAME.md └── gen.go ├── strings ├── main.go └── main_test.go └── time ├── README.md ├── profile001.png ├── profile002.png ├── profile003.png ├── profile004.png ├── profile005.png ├── profile006.png ├── profile007.png ├── tick.go ├── tickHour.png ├── tickNano-2.png ├── tickNano-3.png ├── tickNano-4.png ├── tickNano.png ├── tickSecond-2.png └── tickSecond.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | vendor/ 16 | 17 | .DS_Store 18 | 19 | 20 | aws/cloudfront-func/amazon-cloudfront-functions -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "args": [ 13 | "http_msg_doAndCommit" 14 | ], 15 | "program": "${workspaceFolder}/usecase/dtm/dtm-cases/flash/main.go" 16 | // "program": "${workspaceFolder}/usecase/dtm/quick-start-sample/workflow-http/main.go" 17 | 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /WIREMOCK.md: -------------------------------------------------------------------------------- 1 | # wire mock 2 | 3 | - run docker: 4 | ```sh 5 | docker run -it -d -p 8080:8080 --name wiremock holomekc/wiremock-gui 6 | ``` 7 | 8 | - open ui: 9 | ```txt 10 | http://localhost:8080/__admin/webapp 11 | ``` 12 | 13 | - create url: 14 | link: https://www.youtube.com/watch?v=MtWftxcvbjs -------------------------------------------------------------------------------- /aws/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/aws/.DS_Store -------------------------------------------------------------------------------- /aws/apigw/README.md: -------------------------------------------------------------------------------- 1 | # apigw 2 | ## setup mtls 3 | -------------------------------------------------------------------------------- /aws/apigw/mtls/my_client.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIChjCCAW4CAQAwQTELMAkGA1UEBhMCdm4xEjAQBgNVBAgMCWhvY2hpbWluaDEP 3 | MA0GA1UEBwwGdGh1ZHVjMQ0wCwYDVQQKDARoZHNzMIIBIjANBgkqhkiG9w0BAQEF 4 | AAOCAQ8AMIIBCgKCAQEA0nfwcb0n+0LD14ExzL1TP6ydGvKA4bclXE7hBGsI5B+r 5 | VGHGvlAx3S7Te+S9ilDgPv58InAyZdF/BPCltCMD6da1Kb8XmMQLQcmbyLaXEpj6 6 | PwUsiRVBQ3HnaVFygLKpHOMH9PgMqeD2/rWLGp5rkp4SDzhvTR4ijaTaWVmj2yXk 7 | oPeaCsoVapLelHobLdiXzuqWifvp3yoDZPYrZsI0p+m/W0rhHEkT7S7VF39ECn2D 8 | wOM0j/aOqwxMy4bqndmOYDMZfrBV2nl5XnoSEoMH+dtdZu9lyHefhJGXxPF2b98D 9 | LE78hjZfqHamsKkRDVG2J9UCqz6yUZze0Kr5NcSgbQIDAQABoAAwDQYJKoZIhvcN 10 | AQELBQADggEBAHjFdS/QyTGAOMjyuChy0oW+jjH4t8Qr8DtPN0gTZ+jripE5aAsc 11 | Rbyt+K6/swQFoqc3DYIdkeU+1ZEouXFLMy/zQPXWRwlI515qifNzLuKbfqBJ6VI+ 12 | BQM2NO4td6hgRHeAKv89tViroWxNk1jwGWCfPrkPlCtXVQR7riZYLY+tDFqPHEsu 13 | XSypYRGOxqFzTgcNY52dIgYzi/eFr/9xnVeAxaVLXT8UG7cazYacQ/sYKdDWjjyF 14 | itcqWEqdQ3RH6lMEN280UbNWnGFSU1nMpB+yydq2+bIpX0kTvCuyuCqQ1AThDIY5 15 | WFJeGy2cGtUJ6yE4HwnZMrGAJn3LWu5hjkY= 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /aws/cloudfront-func/README.md: -------------------------------------------------------------------------------- 1 | # Amazon CloudFront Functions 2 | This repository contains example CloudFront functions and instructions to deploy them to CloudFront with Golang 3 | 4 | ## Overview -------------------------------------------------------------------------------- /aws/cloudfront-func/kvs-hmac-sha256/README.md: -------------------------------------------------------------------------------- 1 | ## Verify Per Request using SHA256 HMAC signature 2 | 3 | **CloudFront Functions event type: viewer request** 4 | tool generate: https://www.freeformatter.com/hmac-generator.html 5 | -------------------------------------------------------------------------------- /aws/cloudfront-func/kvs-hmac-sha256/test-objects/valid-hmac.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "context": { 4 | "eventType": "viewer-request" 5 | }, 6 | "viewer": { 7 | "ip": "0.0.0.0" 8 | }, 9 | "request": { 10 | "method": "GET", 11 | "uri": "/index.html", 12 | "headers": { 13 | "signature": { 14 | "value": "af0d733d241fa0a46d803afcb18927756809070428973f409983d0a5f4c0ae63" 15 | }, 16 | "requestid": { 17 | "value": "123" 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /aws/codecommit/README.md: -------------------------------------------------------------------------------- 1 | # aws code commit 2 | 3 | ## code repo 4 | ```sh 5 | git config --global credential.helper '!aws codecommit credential-helper $@' 6 | 7 | git config --global credential.UseHttpPath true 8 | ``` 9 | -------------------------------------------------------------------------------- /aws/lambda/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/aws/lambda/.DS_Store -------------------------------------------------------------------------------- /aws/lambda/README.md: -------------------------------------------------------------------------------- 1 | # lambda 2 | 3 | - requirement: 4 | + node 5 | + npm 6 | + serverless 7 | + aws account 8 | 9 | ## list blog: 10 | - Tạo project bằng serverless framework: https://viblo.asia/p/golang-tao-project-lambda-bang-serverless-EoW4ob9xVml 11 | - Lambda xử lý nhận và return data về cho client: https://viblo.asia/p/go-lambda-apigateway-xu-ly-request-response-5pPLkPY8VRZ 12 | - Lambda CRUD với postgres database: https://viblo.asia/p/go-lambda-crud-voi-postgres-Ny0VGE87JPA -------------------------------------------------------------------------------- /aws/lambda/courses/sharing/.gitignore: -------------------------------------------------------------------------------- 1 | # Serverless directories 2 | .serverless 3 | 4 | # golang output binary directory 5 | bin 6 | 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, build with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | -------------------------------------------------------------------------------- /aws/lambda/courses/sharing/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean deploy 2 | 3 | build: 4 | env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/hello hello/main.go 5 | env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/world world/main.go 6 | 7 | clean: 8 | rm -rf ./bin 9 | 10 | deploy: clean build 11 | sls deploy --verbose 12 | -------------------------------------------------------------------------------- /aws/lambda/crud/.gitignore: -------------------------------------------------------------------------------- 1 | # Serverless directories 2 | .serverless 3 | 4 | # golang output binary directory 5 | bin 6 | 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, build with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | -------------------------------------------------------------------------------- /aws/lambda/crud/Makefile: -------------------------------------------------------------------------------- 1 | # .PHONY: build clean deploy 2 | 3 | build: 4 | env GOARCH=amd64 CGO_ENABLED="0" GOOS=linux go build -ldflags="-s -w" -o bin/create create/main.go 5 | env GOARCH=amd64 CGO_ENABLED="0" GOOS=linux go build -ldflags="-s -w" -o bin/update update/main.go 6 | env GOARCH=amd64 CGO_ENABLED="0" GOOS=linux go build -ldflags="-s -w" -o bin/delete delete/main.go 7 | env GOARCH=amd64 CGO_ENABLED="0" GOOS=linux go build -ldflags="-s -w" -o bin/read read/main.go 8 | clean: 9 | rm -rf ./bin 10 | 11 | deploy: clean build 12 | sls deploy --verbose 13 | -------------------------------------------------------------------------------- /aws/lambda/crud/models/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type UserModel struct { 4 | ID uint `gorm:"primarykey"` 5 | UserName string `gorm:"column:username" bson:"username"` 6 | Email string `gorm:"column:email" bson:"email"` 7 | Phone string `gorm:"column:phone" bson:"phone"` 8 | } 9 | -------------------------------------------------------------------------------- /aws/lambda/crud/sql.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE "users" ( 2 | "id" bigserial, 3 | username character varying(50) COLLATE pg_catalog."default", 4 | name character varying(50) COLLATE pg_catalog."default" NOT NULL, 5 | phone character varying(50) COLLATE pg_catalog."default", 6 | PRIMARY KEY ("id") 7 | ); 8 | -------------------------------------------------------------------------------- /aws/lambda/golang-migration-go1.x-provided.al2/.gitignore: -------------------------------------------------------------------------------- 1 | # Serverless directories 2 | .serverless 3 | 4 | # golang output binary directory 5 | bin 6 | 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, build with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | -------------------------------------------------------------------------------- /aws/lambda/golang-migration-go1.x-provided.al2/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean deploy 2 | 3 | build: 4 | # env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/migration/x86 migration/main.go 5 | # env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/bootstrap migration/main.go 6 | env GOARCH=arm64 GOOS=linux go build -ldflags="-s -w" -o bin/bootstrap migration/main.go 7 | 8 | clean: 9 | rm -rf ./bin 10 | 11 | deploy: clean build 12 | sls deploy --verbose 13 | 14 | zip: 15 | zip -j bin/migration/x86.zip bin/x86 16 | 17 | buildv2: 18 | env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/migration/bootstrap migration/main.go 19 | zip: 20 | zip -j bin/migration.zip bin/migration/bootstrap 21 | -------------------------------------------------------------------------------- /aws/lambda/golang-migration-go1.x-provided.al2/README.md: -------------------------------------------------------------------------------- 1 | # golang migration runtime go1.x -> provided.al2 2 | 3 | ## runtime go1.x 4 | - make file 5 | ``` 6 | env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o x86 migration/main.go 7 | ``` 8 | - serverless 9 | ``` 10 | service: golang-migration 11 | frameworkVersion: '3' 12 | 13 | provider: 14 | name: aws 15 | runtime: go1.x 16 | region: ap-southeast-1 17 | 18 | functions: 19 | hello: 20 | handler: x86 21 | events: 22 | - httpApi: 23 | path: /x86 24 | method: get 25 | 26 | ``` 27 | 28 | ## runtime provided.al2 29 | - make file 30 | ``` 31 | env GOARCH=arm64 GOOS=linux go build -ldflags="-s -w" -o bin/bootstrap migration/main.go 32 | ``` 33 | - serverless 34 | ``` 35 | service: golang-migration 36 | frameworkVersion: '3' 37 | 38 | provider: 39 | name: aws 40 | runtime: provided.al2 # <- change from go1.x to provided.al2 41 | architecture: arm64 # <- change from x86_64 to arm64 42 | region: ap-southeast-1 43 | 44 | functions: 45 | hello: 46 | # handler: bin/x86 # old 47 | handler: bootstrap # new 48 | events: 49 | - httpApi: 50 | # path: /x86 # old 51 | path: /arm64 # new 52 | method: get 53 | ``` -------------------------------------------------------------------------------- /aws/lambda/golang-migration-go1.x-provided.al2/bootstrap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/aws/lambda/golang-migration-go1.x-provided.al2/bootstrap -------------------------------------------------------------------------------- /aws/lambda/golang-migration-go1.x-provided.al2/serverless.yml: -------------------------------------------------------------------------------- 1 | service: golang-migration 2 | frameworkVersion: '3' 3 | 4 | provider: 5 | name: aws 6 | runtime: provided.al2 # <- change from go1.x to provided.al2 7 | architecture: arm64 # <- change from x86_64 to arm64 8 | region: ap-southeast-1 9 | 10 | functions: 11 | hello: 12 | # handler: bin/x86 # old 13 | handler: bootstrap # new 14 | events: 15 | - httpApi: 16 | # path: /x86 # old 17 | path: /arm64 # new 18 | method: get 19 | -------------------------------------------------------------------------------- /aws/lambda/hello-api/.gitignore: -------------------------------------------------------------------------------- 1 | # Serverless directories 2 | .serverless 3 | 4 | # golang output binary directory 5 | bin 6 | 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, build with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | -------------------------------------------------------------------------------- /aws/lambda/hello-api/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean zip deploy 2 | 3 | build: 4 | env GOARCH=arm64 GOOS=linux go build -ldflags="-s -w" -o bin/hello/bootstrap hello/main.go 5 | clean: 6 | rm -rf ./bin 7 | deploy: clean build zip 8 | sls deploy --verbose 9 | zip: 10 | zip -j bin/hello.zip bin/hello/bootstrap 11 | -------------------------------------------------------------------------------- /aws/lambda/hello-api/README.md: -------------------------------------------------------------------------------- 1 | # Golang Hello API -------------------------------------------------------------------------------- /aws/lambda/hello-api/serverless.yml: -------------------------------------------------------------------------------- 1 | service: golang-hello-api 2 | frameworkVersion: '3' 3 | 4 | provider: 5 | name: aws 6 | runtime: provided.al2 7 | architecture: arm64 8 | region: ap-southeast-1 9 | package: 10 | individually: true 11 | patterns: 12 | - "!./**" 13 | - ./bin/** 14 | 15 | functions: 16 | hello: 17 | handler: bootstrap 18 | package: 19 | artifact: bin/hello.zip 20 | timeout: 6 21 | memorySize: 256 22 | description: function hello api 23 | events: 24 | - httpApi: 25 | path: /hello 26 | method: get 27 | -------------------------------------------------------------------------------- /aws/lambda/images/.gitignore: -------------------------------------------------------------------------------- 1 | # Serverless directories 2 | .serverless 3 | 4 | # golang output binary directory 5 | bin 6 | 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, build with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | -------------------------------------------------------------------------------- /aws/lambda/images/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean deploy 2 | 3 | build: 4 | env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/insertImage functions/insertImage/main.go 5 | env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/getImage functions/getImage/main.go 6 | 7 | clean: 8 | rm -rf ./bin 9 | 10 | deploy: clean build 11 | sls deploy --verbose 12 | -------------------------------------------------------------------------------- /aws/lambda/images/functions/authorizer/authorizer.yml: -------------------------------------------------------------------------------- 1 | authorizerOpenapi: 2 | runtime: python3.8 3 | handler: functions/authorizer/openapi-lambda-auth.lambda_handler 4 | timeout: 3 5 | memorySize: 128 6 | description: authorizer openapi 7 | role: '${file(envs/authorizer/${opt:stage, sls:stage}.json):ROLE}' 8 | -------------------------------------------------------------------------------- /aws/lambda/images/serverless.yml: -------------------------------------------------------------------------------- 1 | 2 | service: blog-images 3 | frameworkVersion: '3' 4 | 5 | provider: 6 | name: aws 7 | runtime: go1.x 8 | region: ap-southeast-1 9 | 10 | environment: 11 | S3_BUCKET: "s3-south-image-blog" 12 | IMAGE_BLOG: "image-blog/" 13 | # iam: 14 | # role: blog-image-action-s3-from-lambda 15 | 16 | package: 17 | patterns: 18 | - '!./**' 19 | - ./bin/** 20 | 21 | functions: 22 | insertImage: 23 | handler: bin/insertImage 24 | timeout: 3 25 | memorySize: 512 26 | description: insert image of blog to s3 27 | events: 28 | - http: 29 | path: /image/insert 30 | method: post 31 | # private: true 32 | environment: 33 | TEST_ENV: "test" 34 | TEST_1_ENV: "tes11t" 35 | getImage: 36 | handler: bin/getImage 37 | timeout: 3 38 | memorySize: 512 39 | description: get image of blog to s3 40 | events: 41 | - http: 42 | path: /image/get 43 | method: post 44 | # private: true 45 | environment: 46 | TEST_ENV: "test" 47 | TEST_1_ENV: "tes11t" 48 | -------------------------------------------------------------------------------- /aws/lambda/lambda-go-1-2/.gitignore: -------------------------------------------------------------------------------- 1 | # Serverless directories 2 | .serverless 3 | 4 | # golang output binary directory 5 | bin 6 | 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, build with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | -------------------------------------------------------------------------------- /aws/lambda/lambda-go-1-2/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean deploy 2 | 3 | build: 4 | env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/hello hello/main.go 5 | clean: 6 | rm -rf ./bin 7 | 8 | deploy: clean build 9 | sls deploy --verbose 10 | -------------------------------------------------------------------------------- /aws/lambda/lambda-go-1-2/README.MD: -------------------------------------------------------------------------------- 1 | # lambda simple: 2 | - create simple lambda function 3 | - read blog: https://viblo.asia/p/golang-aws-lambda-thong-qua-serverless-framework-phan-1-EoW4ob9xVml 4 | 5 | 6 | aws lambda create-event-source-mapping --function-name lambda-go-dev-receive --batch-size 10 --region us-east-1 \ 7 | --event-source-arn arn:aws:sqs:us-east-1:064038607558:sqs-demo 8 | 9 | 10 | aws lambda list-event-source-mappings --function-name lambda-go-dev-receive --region us-east-1 \ 11 | --event-source-arn arn:aws:sqs:us-east-1:064038607558:sqs-demo -------------------------------------------------------------------------------- /aws/lambda/lambda-go-1-2/serverless.yml: -------------------------------------------------------------------------------- 1 | service: lambda-go 2 | frameworkVersion: '3' 3 | 4 | provider: 5 | name: aws 6 | runtime: go1.x 7 | timeout: 6 8 | memorySize: 256 9 | 10 | environment: 11 | env_test: "value-test" 12 | 13 | package: 14 | patterns: 15 | - '!./**' 16 | - ./bin/** 17 | 18 | functions: 19 | hello: 20 | handler: bin/hello 21 | events: 22 | - httpApi: 23 | path: /hello 24 | method: post -------------------------------------------------------------------------------- /aws/lambda/lambda-go-3/.gitignore: -------------------------------------------------------------------------------- 1 | # Serverless directories 2 | .serverless 3 | 4 | # golang output binary directory 5 | bin 6 | 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, build with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | -------------------------------------------------------------------------------- /aws/lambda/lambda-go-3/Makefile: -------------------------------------------------------------------------------- 1 | # .PHONY: build clean deploy 2 | 3 | build: 4 | env GOARCH=amd64 CGO_ENABLED="0" GOOS=linux go build -ldflags="-s -w" -o bin/create create/main.go 5 | env GOARCH=amd64 CGO_ENABLED="0" GOOS=linux go build -ldflags="-s -w" -o bin/update update/main.go 6 | env GOARCH=amd64 CGO_ENABLED="0" GOOS=linux go build -ldflags="-s -w" -o bin/delete delete/main.go 7 | env GOARCH=amd64 CGO_ENABLED="0" GOOS=linux go build -ldflags="-s -w" -o bin/read read/main.go 8 | clean: 9 | rm -rf ./bin 10 | 11 | deploy: clean build 12 | sls deploy --verbose 13 | -------------------------------------------------------------------------------- /aws/lambda/lambda-go-3/README.MD: -------------------------------------------------------------------------------- 1 | # lambda simple: 2 | - create simple lambda function 3 | - read blog: https://viblo.asia/p/golang-aws-lambda-thong-qua-serverless-framework-phan-1-EoW4ob9xVml 4 | - handle request and response in api: https://viblo.asia/p/golang-aws-lambda-thong-qua-serverless-framework-phan-2-5pPLkPY8VRZ 5 | 6 | ## Handle API CRUD 7 | - Create table user với script: 8 | ``` 9 | CREATE TABLE "users" ( 10 | "id" bigserial, 11 | user_name character varying(50) COLLATE pg_catalog."default", 12 | name character varying(50) COLLATE pg_catalog."default" NOT NULL, 13 | phone character varying(50) COLLATE pg_catalog."default", 14 | PRIMARY KEY ("id") 15 | ); 16 | ``` 17 | 18 | - Add new user: 19 | -------------------------------------------------------------------------------- /aws/lambda/lambda-go-3/models/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type UserModel struct { 4 | ID uint `gorm:"primarykey"` 5 | UserName string `gorm:"column:user_name" bson:"user_name"` 6 | Name string `gorm:"column:name" bson:"name"` 7 | Phone string `gorm:"column:phone" bson:"phone"` 8 | } 9 | -------------------------------------------------------------------------------- /aws/lambda/lambda-go-3/serverless.yml: -------------------------------------------------------------------------------- 1 | service: lambda-go 2 | frameworkVersion: '3' 3 | 4 | provider: 5 | name: aws 6 | runtime: go1.x 7 | timeout: 6 8 | memorySize: 256 9 | region: ap-southeast-1 10 | 11 | environment: 12 | DB_USER: "" 13 | DB_PASS: "" 14 | DB_HOST: "" 15 | DB_SERVICE: "" 16 | 17 | package: 18 | patterns: 19 | - '!./**' 20 | - ./bin/** 21 | 22 | functions: 23 | create: 24 | handler: bin/create 25 | events: 26 | - httpApi: 27 | path: /create 28 | method: post 29 | delete: 30 | handler: bin/delete 31 | events: 32 | - httpApi: 33 | path: /delete 34 | method: post 35 | read: 36 | handler: bin/read 37 | events: 38 | - httpApi: 39 | path: /read 40 | method: get 41 | update: 42 | handler: bin/update 43 | events: 44 | - httpApi: 45 | path: /update 46 | method: post -------------------------------------------------------------------------------- /aws/lambda/lambda-go-3/sql.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE "users" ( 2 | "id" bigserial, 3 | user_name character varying(50) COLLATE pg_catalog."default", 4 | name character varying(50) COLLATE pg_catalog."default" NOT NULL, 5 | phone character varying(50) COLLATE pg_catalog."default", 6 | PRIMARY KEY ("id") 7 | ); 8 | -------------------------------------------------------------------------------- /aws/s3/delete.go: -------------------------------------------------------------------------------- 1 | package s3 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/aws/aws-sdk-go/aws" 7 | "github.com/aws/aws-sdk-go/aws/session" 8 | "github.com/aws/aws-sdk-go/service/s3" 9 | ) 10 | 11 | func (*AwsS3) DeleteFile(key string) error { 12 | session := session.Must(session.NewSession()) 13 | svc := s3.New(session) 14 | filename := configS3.PathPrefix + "/" + key 15 | bucket := configS3.Bucket 16 | 17 | _, err := svc.DeleteObject(&s3.DeleteObjectInput{ 18 | Bucket: aws.String(bucket), 19 | Key: aws.String(filename), 20 | }) 21 | if err != nil { 22 | return errors.New("function svc.DeleteObject() failed, err:" + err.Error()) 23 | } 24 | 25 | _ = svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{ 26 | Bucket: aws.String(bucket), 27 | Key: aws.String(filename), 28 | }) 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /aws/s3/error.html: -------------------------------------------------------------------------------- 1 |

this page error

-------------------------------------------------------------------------------- /aws/s3/image/handler-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/aws/s3/image/handler-upload.png -------------------------------------------------------------------------------- /aws/s3/image/upload-success-s3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/aws/s3/image/upload-success-s3.png -------------------------------------------------------------------------------- /aws/s3/image/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/aws/s3/image/upload.png -------------------------------------------------------------------------------- /aws/s3/index.html: -------------------------------------------------------------------------------- 1 |

this home page

-------------------------------------------------------------------------------- /aws/s3/s3.go: -------------------------------------------------------------------------------- 1 | package s3 2 | 3 | type AwsS3 struct{} 4 | type AwsS3Config struct { 5 | Bucket string 6 | Region string 7 | Endpoint string 8 | SecretID string 9 | SecretKey string 10 | BaseURL string 11 | PathPrefix string 12 | S3ForcePathStyle bool 13 | DisableSSL bool 14 | } 15 | 16 | var configS3 = AwsS3Config{ 17 | Bucket: "ducnp5", 18 | Region: "ap-southeast-1", 19 | Endpoint: "endpoint", 20 | SecretID: "xxx", 21 | SecretKey: "xxx", 22 | BaseURL: "opendev", 23 | PathPrefix: "", 24 | S3ForcePathStyle: true, 25 | DisableSSL: true, 26 | } 27 | -------------------------------------------------------------------------------- /aws/s3/upload.go: -------------------------------------------------------------------------------- 1 | package s3 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "mime/multipart" 7 | "time" 8 | 9 | "github.com/aws/aws-sdk-go/aws" 10 | "github.com/aws/aws-sdk-go/aws/session" 11 | "github.com/aws/aws-sdk-go/service/s3/s3manager" 12 | ) 13 | 14 | func (*AwsS3) UploadFile(file *multipart.FileHeader) (string, string, error) { 15 | session := session.Must(session.NewSession()) 16 | 17 | uploader := s3manager.NewUploader(session) 18 | 19 | fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename) 20 | filename := configS3.PathPrefix + "/" + fileKey 21 | f, openError := file.Open() 22 | if openError != nil { 23 | return "", "", errors.New("function file.Open() failed, err:" + openError.Error()) 24 | } 25 | defer f.Close() 26 | 27 | _, err := uploader.Upload(&s3manager.UploadInput{ 28 | Bucket: aws.String(configS3.Bucket), 29 | Key: aws.String(filename), 30 | Body: f, 31 | }) 32 | if err != nil { 33 | return "", "", err 34 | } 35 | 36 | return configS3.BaseURL + "/" + filename, fileKey, nil 37 | } 38 | -------------------------------------------------------------------------------- /aws/secretsmanager/README.md: -------------------------------------------------------------------------------- 1 | 2 | # aws secrets manager 3 | 4 | aws secretsmanager list-secrets 5 | 6 | aws secretsmanager list-secrets \ 7 | --query "SecretList[?Tags[?Key=='aws secretsmanager list-secrets --filter Key="asm-apse1-cicdhdbank/dev/nhdt/fakeapp/PvoilSecret"']]" 8 | 9 | aws secretsmanager list-secrets --filter Key="asm-apse1-cicdhdbank/dev/nhdt/fakeapp/PvoilSecret" 10 | 11 | aws secretsmanager create-secret --name asm-apse1-cicdhdbank/dev/nhdt/fakeapp/DatabasePassword \ 12 | --description "My test secret created with the CLI." \ 13 | --secret-string "{\"user\":\"diegor\",\"password\":\"EXAMPLE-PASSWORD\"}" 14 | 15 | 16 | 17 | aws secretsmanager create-secret --name asm-apse1-cicdhdbank/dev/nhdt/fakeapp/PvoilSecret \ 18 | --description "My test secret created with the CLI." \ 19 | --secret-string "{\"user\":\"diegor\",\"password\":\"EXAMPLE-PASSWORD\"}" 20 | 21 | aws secretsmanager list-secrets | jq '.SecretList[] | select(.Name | contains("fakeapp"))' 22 | -------------------------------------------------------------------------------- /aws/sqs/README.md: -------------------------------------------------------------------------------- 1 | # simple queue service 2 | 3 | ## prerequisites 4 | - install aws-go sdk 5 | ```go 6 | go get -u github.com/aws/aws-sdk-go/ 7 | ``` 8 | - aws credentials: 9 | 10 | ## how to create a new sqs queue? 11 | -------------------------------------------------------------------------------- /aws/sqs/lambda/.gitignore: -------------------------------------------------------------------------------- 1 | # Serverless directories 2 | .serverless 3 | 4 | # golang output binary directory 5 | bin 6 | 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, build with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | -------------------------------------------------------------------------------- /aws/sqs/lambda/Makefile: -------------------------------------------------------------------------------- 1 | # .PHONY: build clean deploy 2 | 3 | build: 4 | env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/push push/main.go 5 | env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/receive receive/main.go 6 | clean: 7 | rm -rf ./bin 8 | 9 | deploy: clean build 10 | sls deploy --verbose 11 | -------------------------------------------------------------------------------- /aws/sqs/lambda/README.MD: -------------------------------------------------------------------------------- 1 | # lambda simple: 2 | - create simple lambda function 3 | - read blog: https://viblo.asia/p/golang-aws-lambda-thong-qua-serverless-framework-phan-1-EoW4ob9xVml 4 | 5 | 6 | aws lambda create-event-source-mapping --function-name lambda-go-dev-receive --batch-size 10 --region us-east-1 \ 7 | --event-source-arn arn:aws:sqs:us-east-1:064038607558:sqs-demo 8 | 9 | 10 | aws lambda list-event-source-mappings --function-name lambda-go-dev-receive --region us-east-1 \ 11 | --event-source-arn arn:aws:sqs:us-east-1:064038607558:sqs-demo -------------------------------------------------------------------------------- /aws/sqs/lambda/serverless.yml: -------------------------------------------------------------------------------- 1 | service: lambda-sqs 2 | frameworkVersion: '3' 3 | 4 | provider: 5 | name: aws 6 | runtime: go1.x 7 | timeout: 6 8 | memorySize: 256 9 | 10 | environment: 11 | env_test: "value-test" 12 | 13 | package: 14 | patterns: 15 | - '!./**' 16 | - ./bin/** 17 | 18 | functions: 19 | push: 20 | handler: bin/push 21 | events: 22 | - httpApi: 23 | path: /push 24 | method: post 25 | receive: 26 | handler: bin/receive 27 | # events: 28 | # - httpApi: 29 | # path: /push 30 | # method: post -------------------------------------------------------------------------------- /aws/sqs/main.go: -------------------------------------------------------------------------------- 1 | package sqs 2 | -------------------------------------------------------------------------------- /concurrency/fanInfanOut.go: -------------------------------------------------------------------------------- 1 | package concurrency 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func MainFanInFanOut() { 10 | 11 | } 12 | 13 | func faninfanout() { 14 | rand := func() interface{} { return rand.Intn(50000000) } 15 | done := make(chan interface{}) 16 | defer close(done) 17 | start := time.Now() 18 | randIntStream := toInt(done, repeatFn(done, rand)) 19 | fmt.Println("Primes:") 20 | for prime := range take(done, primeFinder(done, randIntStream), 10) { 21 | fmt.Printf("\t%d\n", prime) 22 | } 23 | fmt.Printf("Search took: %v", time.Since(start)) 24 | } 25 | 26 | func repeatFn(done <-chan interface{}, fn func() interface{}) <-chan interface{} { 27 | valueStream := make(chan interface{}) 28 | go func() { 29 | defer close(valueStream) 30 | for { 31 | select { 32 | case <-done: 33 | return 34 | case valueStream <- fn(): 35 | } 36 | } 37 | }() 38 | return valueStream 39 | } 40 | -------------------------------------------------------------------------------- /concurrency/leak.go: -------------------------------------------------------------------------------- 1 | package concurrency 2 | 3 | import "fmt" 4 | 5 | // MainLeaks goroutine leaks memory 6 | func MainLeaks() { 7 | Leak() 8 | Leak() 9 | } 10 | 11 | func Leak() { 12 | doWork := func(strings <-chan string) <-chan interface{} { 13 | completed := make(chan interface{}) 14 | go func() { 15 | defer func() { 16 | fmt.Println("doWork exited.") 17 | }() 18 | defer func() { 19 | close(completed) 20 | }() 21 | for s := range strings { 22 | // Do something interesting 23 | fmt.Println(s) 24 | } 25 | }() 26 | fmt.Println("end function doWork") 27 | return completed 28 | } 29 | fmt.Println("dowork") 30 | doWork(nil) 31 | // Perhaps more work is done here 32 | fmt.Println("Done.") 33 | } 34 | -------------------------------------------------------------------------------- /concurrency/patterns/fanInfanOut/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | const ( 9 | numProducers = 2 10 | numConsumers = 2 11 | ) 12 | 13 | func producer(id int, ch chan<- int, wg *sync.WaitGroup) { 14 | defer wg.Done() 15 | for i := 1; i <= 5; i++ { 16 | ch <- i 17 | fmt.Printf("Fanout Producer %d produced %d\n", id, i) 18 | } 19 | } 20 | 21 | func consumer(id int, in <-chan int, out chan<- int, wg *sync.WaitGroup) { 22 | defer wg.Done() 23 | for v := range in { 24 | out <- v * 2 25 | fmt.Printf("FanIn Consumer %d processed %d\n", id, v) 26 | } 27 | } 28 | 29 | func main() { 30 | 31 | input := make(chan int, 10) 32 | output := make(chan int, 10) 33 | var wg sync.WaitGroup 34 | for i := 1; i <= numProducers; i++ { 35 | wg.Add(1) 36 | go producer(i, input, &wg) 37 | } 38 | wg.Wait() 39 | close(input) 40 | for i := 1; i <= numConsumers; i++ { 41 | wg.Add(1) 42 | go consumer(i, input, output, &wg) 43 | } 44 | wg.Wait() 45 | close(output) 46 | for result := range output { 47 | fmt.Println("Ketqua:", result) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /concurrency/patterns/pipeline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func pipelineStep1(nums []int) <-chan int { 6 | out := make(chan int) 7 | go func() { 8 | for _, n := range nums { 9 | fmt.Println("pipelineStep1", n) 10 | out <- n 11 | } 12 | close(out) 13 | }() 14 | return out 15 | } 16 | 17 | func pipelineStep2(in <-chan int) <-chan int { 18 | out := make(chan int) 19 | go func() { 20 | for n := range in { 21 | fmt.Println("pipelineStep2", n) 22 | out <- n * 2 23 | } 24 | close(out) 25 | }() 26 | return out 27 | } 28 | 29 | func pipelineStep3(in <-chan int) <-chan int { 30 | out := make(chan int) 31 | go func() { 32 | for n := range in { 33 | fmt.Println("pipelineStep3", n) 34 | out <- n + 1 35 | } 36 | close(out) 37 | }() 38 | return out 39 | } 40 | 41 | func main() { 42 | nums := []int{1, 2, 3, 4, 5} 43 | c1 := pipelineStep1(nums) 44 | c2 := pipelineStep2(c1) 45 | c3 := pipelineStep3(c2) 46 | for result := range c3 { 47 | fmt.Println(result) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /concurrency/patterns/rateLimit/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | rate := time.Millisecond * 500 10 | ticker := time.NewTicker(rate) 11 | defer ticker.Stop() 12 | 13 | requests := make(chan int, 10) 14 | for i := 1; i <= 10; i++ { 15 | requests <- i 16 | } 17 | close(requests) 18 | 19 | for req := range requests { 20 | <-ticker.C // Esperar el siguiente tick 21 | fmt.Println("Processing event:", req) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /concurrency/patterns/selectTimeout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | c := make(chan string) 10 | 11 | go func() { 12 | time.Sleep(2 * time.Second) 13 | c <- "result" 14 | }() 15 | 16 | select { 17 | case res := <-c: 18 | fmt.Println("Received:", res) 19 | case <-time.After(1 * time.Second): 20 | fmt.Println("Timeout") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /concurrency/patterns/semaphore/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func worker(id int, sem chan struct{}, wg *sync.WaitGroup) { 10 | defer wg.Done() 11 | sem <- struct{}{} // 12 | fmt.Printf("Worker %d starting\n", id) 13 | time.Sleep(time.Second) 14 | fmt.Printf("Worker %d done\n", id) 15 | <-sem // 16 | } 17 | 18 | func main() { 19 | const numWorkers = 5 20 | const maxConcurrent = 2 21 | sem := make(chan struct{}, maxConcurrent) 22 | var wg sync.WaitGroup 23 | 24 | for i := 1; i <= numWorkers; i++ { 25 | wg.Add(1) 26 | go worker(i, sem, &wg) 27 | } 28 | 29 | wg.Wait() 30 | } 31 | -------------------------------------------------------------------------------- /concurrency/patterns/workerpool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | const ( 10 | numberJobConst = 4 11 | numWorkersConst = 2 12 | ) 13 | 14 | func doWorker(id int, jobs <-chan int, kqs chan<- int, wg *sync.WaitGroup) { 15 | for job := range jobs { 16 | fmt.Printf("Do Worker %d started job %d\n", id, job) 17 | time.Sleep(time.Second) 18 | fmt.Printf("Do Worker %d finished job %d\n", id, job) 19 | kqs <- job * 2 20 | } 21 | wg.Done() 22 | } 23 | 24 | func main() { 25 | jobs := make(chan int, numberJobConst) 26 | kqs := make(chan int, numberJobConst) 27 | var wg sync.WaitGroup 28 | 29 | for i := 1; i <= numWorkersConst; i++ { 30 | wg.Add(1) 31 | go doWorker(i, jobs, kqs, &wg) 32 | } 33 | 34 | for j := 1; j <= numberJobConst; j++ { 35 | jobs <- j 36 | } 37 | close(jobs) 38 | 39 | wg.Wait() 40 | close(kqs) 41 | 42 | for kq := range kqs { 43 | fmt.Println("Worker Result:", kq) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /concurrency/singleflight/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | 8 | "golang.org/x/sync/singleflight" 9 | ) 10 | 11 | func init() { 12 | rand.Seed(time.Now().UnixNano()) 13 | } 14 | 15 | var ( 16 | concurrencyControl = &singleflight.Group{} 17 | n = 10 18 | ) 19 | 20 | func locking(_ int) (int64, error) { 21 | now := time.Now() 22 | randomNumber := rand.Intn(1000) + 1 23 | time.Sleep(time.Millisecond * time.Duration(randomNumber)) 24 | return time.Since(now).Milliseconds(), nil 25 | } 26 | func main() { 27 | now := time.Now() 28 | keyS := "flight" 29 | for i := 1; i <= n; i++ { 30 | go func() { 31 | value, err, s := concurrencyControl.Do(keyS, func() (interface{}, error) { 32 | return locking(i) 33 | }) 34 | if err != nil { 35 | fmt.Printf("Goroutine %d: error: %v\n", i, err) 36 | return 37 | } 38 | fmt.Printf("Goroutine %d: result: %v (shared: %v)\n", i, value, s) 39 | 40 | }() 41 | } 42 | time.Sleep(time.Second * 5) 43 | fmt.Println("Done", time.Since(now).Milliseconds()) 44 | } 45 | -------------------------------------------------------------------------------- /concurrency/singleflight/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/concurrency/singleflight/output.png -------------------------------------------------------------------------------- /concurrency/syncCond.go: -------------------------------------------------------------------------------- 1 | package concurrency 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func CondExample1() { 10 | c := sync.NewCond(&sync.Mutex{}) 11 | queue := make([]int, 0, 10) 12 | removeFromQueue := func(delay time.Duration, i int) { 13 | time.Sleep(delay) 14 | c.L.Lock() 15 | fmt.Println("before remove:", queue) 16 | queue = queue[1:] 17 | fmt.Println("after remove:", queue) 18 | c.L.Unlock() 19 | c.Signal() 20 | } 21 | for i := 0; i < 10; i++ { 22 | fmt.Println("start loop;", i) 23 | c.L.Lock() 24 | for len(queue) == 2 { 25 | time.Sleep(time.Second * 2) 26 | fmt.Println("len equal 2, waiting", i) 27 | c.Wait() 28 | } 29 | fmt.Println("Adding to queue", i) 30 | queue = append(queue, i) 31 | go removeFromQueue(1*time.Second, i) 32 | c.L.Unlock() 33 | fmt.Println() 34 | fmt.Println() 35 | } 36 | fmt.Println("after processing, len queue:", len(queue), queue) 37 | } 38 | -------------------------------------------------------------------------------- /database/index/note.txt: -------------------------------------------------------------------------------- 1 | SHOW max_connections; 2 | 3 | SELECT current_setting('max_connections'); 4 | 5 | EXPLAIN ANALYZE 6 | select * from info where uuid = '05aee2f5-aa10-4a15-9e2c-44166380e3a4' 7 | and ref_id =3; 8 | 9 | select count(*) from info ; 10 | 11 | CREATE INDEX idx_info_ref_id_hash ON info USING HASH(ref_id); 12 | 13 | 14 | 15 | CREATE INDEX idx_info_ref_id ON info (ref_id); 16 | 17 | -- delete from info; 18 | SELECT md5(random()::text); 19 | 20 | SELECT uuid_in(overlay(overlay(md5(random()::text || ':' || random()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring); 21 | 22 | DO $FN$ 23 | BEGIN 24 | FOR counter IN 10001..12000 LOOP 25 | insert into info(id, email, location, name,uuid, age,ref_id) values( 26 | counter, 27 | md5(random()::text), 28 | md5(random()::text), 29 | md5(random()::text), 30 | uuid_in(overlay(overlay(md5(random()::text || ':' || random()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring), 31 | counter, 32 | 1 33 | ); 34 | END LOOP; 35 | END; 36 | $FN$ 37 | -------------------------------------------------------------------------------- /dtm/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/dtm/.gitignore -------------------------------------------------------------------------------- /dtm/dtm-cases/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /dtm/dtm-cases/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 DTM Development and Communities 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dtm/dtm-cases/README-cn.md: -------------------------------------------------------------------------------- 1 | 简体中文 | [English](./README.md) 2 | 3 | # dtm 的典型案例 4 | 5 | ## 订单应用 6 | dtm 可以大幅简化非单体的订单系统,详情参见 [order](./order/README-cn.md) 7 | 8 | ## 秒杀应用 9 | dtm 可以应用于精准扣库存的秒杀系统,详情参见[flash](./flash/README-cn.md) 10 | 11 | ## 缓存应用 12 | dtm 可以应用缓存一致性,详情参加[cache](./cache/README-cn.md) -------------------------------------------------------------------------------- /dtm/dtm-cases/README.md: -------------------------------------------------------------------------------- 1 | English | [简体中文](./README-cn.md) 2 | 3 | # Application of dtm 4 | 5 | ## Order Applications 6 | dtm can significantly simplify the order system for non-monolithic order system, see [order](./order) 7 | 8 | ## Flash-sale Applications 9 | dtm can be used for precise stock deduction of flash-sale, see [flash](./flash) 10 | 11 | ## Caching applications 12 | dtm can be applied to ensure cache consistency, see [cache](./cache) -------------------------------------------------------------------------------- /dtm/dtm-cases/cache/demo/api-shared.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import ( 4 | "github.com/dtm-labs/dtm-cases/utils" 5 | "github.com/dtm-labs/dtmcli" 6 | "github.com/dtm-labs/dtmcli/logger" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func init() { 11 | 12 | BusiApp.POST(BusiAPI+"/deleteKey", utils.WrapHandler(func(c *gin.Context) interface{} { 13 | body := MustMapBodyFrom(c) 14 | key := body["key"].(string) 15 | logger.Infof("deleting key: %s", key) 16 | _, err := rdb.Del(rdb.Context(), key).Result() 17 | return err 18 | })) 19 | BusiApp.GET(BusiAPI+"/queryPrepared", utils.WrapHandler(func(c *gin.Context) interface{} { 20 | bb, err := dtmcli.BarrierFromQuery(c.Request.URL.Query()) 21 | if err == nil { 22 | err = bb.QueryPrepared(db) 23 | } 24 | return err 25 | })) 26 | BusiApp.POST(BusiAPI+"/deleteCache", utils.WrapHandler(func(c *gin.Context) interface{} { 27 | body := MustMapBodyFrom(c) 28 | mode := body["mode"].(string) 29 | if mode == "delete" { 30 | return rdb.Del(rdb.Context(), body["key"].(string)) 31 | } else { 32 | return dc.TagAsDeleted(body["key"].(string)) 33 | } 34 | })) 35 | } 36 | -------------------------------------------------------------------------------- /dtm/dtm-cases/cache/demo/main.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/dtm-labs/dtmcli/logger" 9 | "github.com/dtm-labs/rockscache" 10 | "github.com/gin-gonic/gin" 11 | _ "github.com/go-sql-driver/mysql" 12 | ) 13 | 14 | var DtmServer = "http://localhost:36789/api/dtmsvr" 15 | 16 | const BusiAPI = "/api/busi" 17 | const BusiPort = 8081 18 | 19 | var BusiUrl = fmt.Sprintf("http://localhost:%d%s", BusiPort, BusiAPI) 20 | 21 | var BusiApp = gin.Default() 22 | 23 | func Main() { 24 | gin.SetMode(gin.ReleaseMode) 25 | rockscache.SetVerbose(true) 26 | logger.InitLog("debug") 27 | startSvr() 28 | time.Sleep(200 * time.Millisecond) 29 | select {} 30 | } 31 | 32 | func startSvr() { 33 | log.Printf("cache examples listening at %d", BusiPort) 34 | go BusiApp.Run(fmt.Sprintf(":%d", BusiPort)) 35 | time.Sleep(100 * time.Millisecond) 36 | } 37 | -------------------------------------------------------------------------------- /dtm/dtm-cases/cache/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/dtm-labs/dtm-cases/cache/demo" 4 | 5 | func main() { 6 | demo.Main() 7 | } 8 | -------------------------------------------------------------------------------- /dtm/dtm-cases/cache/table.sql: -------------------------------------------------------------------------------- 1 | drop database IF EXISTS cache1; 2 | create database cache1; 3 | use cache1; 4 | drop table IF EXISTS ver; 5 | 6 | create table ver ( 7 | k VARCHAR(100) PRIMARY KEY, 8 | v VARCHAR(100) default '', 9 | time_cost varchar(45) default 0 10 | ); -------------------------------------------------------------------------------- /dtm/dtm-cases/flash/README-cn.md: -------------------------------------------------------------------------------- 1 | 简体中文 | [English](./README.md) 2 | 3 | # 秒杀应用 4 | 此项目可以结合dtm文档中的 [秒杀应用](https://dtm.pub/app/flash.html)阅读 5 | 6 | ## 概述 7 | 本项目主要演示了dtm如何应用于秒杀系统,将演示秒杀系统中及时发生进程crash,也能够保证精准的扣减库存并创建精准数量的订单。 8 | 9 | #### 启动dtm 10 | [快速启动dtm](https://dtm.pub/guide/install.html) 11 | 12 | #### 运行本例子 13 | `go run main.go` 14 | 15 | #### 发起订单请求 16 | - 发起一个正常完成的秒杀请求 `curl http://localhost:8081/api/busi/flashSales` 17 | - 发起一个完成库存扣减就crash的秒杀请求 `curl http://localhost:8081/api/busi/flashSales-crash` 大约等待十多秒之后,订单创建完成,不受影响 18 | 19 | #### 模拟秒杀活动 20 | `curl http://localhost:8081/api/busi/flashSales-batch` 21 | 22 | 用户发起这个模拟秒杀活动的请求之后,本例子会做如下事情: 23 | 1. 重置各项变量:将库存设置为4,将已创建的订单数重置为0 24 | 2. 发起一个扣减完库存就宕机的请求,然后睡眠0.5s,保证进行下一步前,库存已扣减到3 25 | 3. 发起10个并发的秒杀请求,然后睡眠0.5s,保证进行下一步前,这10个秒杀请求已处理 26 | 4. 此时输出已经创建的订单数量为3,这三个订单都是步骤3中产生的 27 | 5. 大约等待3~5秒,可以看到创建的订单数量变为4,这是因为步骤2里面的全局事务超时检查,最后成功,创建了第4个订单 28 | 29 | 结论:本示例的方法,可以保证Redis中的库存和数据库中的订单,最终严格一致,无需手动校准数据 30 | 31 | -------------------------------------------------------------------------------- /dtm/dtm-cases/order/README-cn.md: -------------------------------------------------------------------------------- 1 | 简体中文 | [English](./README.md) 2 | 3 | # 订单应用 4 | 5 | 此项目可以结合dtm文档中的 [订单应用](https://dtm.pub/app/order.html)阅读 6 | 7 | ## 概述 8 | 本项目主要演示了dtm如何应用于非单体的订单系统,保证订单中的多个步骤,能够最终“原子”执行,保证最终一致性。 9 | 10 | #### 启动dtm 11 | [快速启动dtm](https://dtm.pub/guide/install.html) 12 | 13 | #### 运行本例子 14 | `go run main.go` 15 | 16 | #### 发起订单请求 17 | - 发起一个正常订单 `curl http://localhost:8081/api/fireSucceed` 18 | - 发起一个因库存不足的回滚订单 `curl http://localhost:8081/api/fireFailed` 19 | - 发起一个因扣减优惠券失败而回滚的订单 `curl http://localhost:8081/api/fireFailedCoupon` 20 | 21 | 以下几点说明一下: 22 | 1. fireFailed 请求,是因为库存不足而失败,此时全局事务会回滚。回滚时会进行库存的回滚操作,此时库存回滚时发生了一个空补偿,在实际操作中不会进行库存相关的业务操作 23 | 2. fireFailedCoupon 请求,是因为扣减优惠券不成功而失败,回滚时会进行库存的回滚操作,此时库存回滚时发生了一个正常补偿,在实际操作中会进行库存相关的业务操作 24 | 3. 开发人员无需关心是否空补偿,开发者只需要关心如何扣减库存和回滚库存,是否空补偿,以及如何回滚空补偿等,都会有dtm框架进行自动处理。 25 | 26 | #### 目录说明 27 | 本项目有以下内容 28 | - main.go: 主程序文件 29 | - service目录:相关各个服务文件 30 | - conf目录:相关配置 31 | - common目录:多个服务共享的代码 32 | - order.sql: 创建本例子所需的订单系统表 33 | -------------------------------------------------------------------------------- /dtm/dtm-cases/order/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "database/sql" 5 | 6 | "github.com/dtm-labs/dtm-cases/order/conf" 7 | "github.com/dtm-labs/dtmcli/dtmimp" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func DBGet() *sql.DB { 12 | db, err := dtmimp.PooledDB(conf.DBConf) 13 | if err != nil { 14 | panic(err) 15 | } 16 | return db 17 | } 18 | 19 | type Req struct { 20 | UserID int `json:"user_id"` 21 | OrderID string `json:"order_id"` 22 | Amount int `json:"amount"` // money amount 23 | ProductID int `json:"product_id"` 24 | ProductCount int `json:"product_count"` // how many product to order 25 | CouponID int `json:"coupon_id"` // optional 26 | } 27 | 28 | func MustGetReq(c *gin.Context) *Req { 29 | var req Req 30 | err := c.BindJSON(&req) 31 | if err != nil { 32 | panic(err) 33 | } 34 | if req.UserID == 0 { 35 | panic("user_id not specified") 36 | } 37 | if req.OrderID == "" { 38 | panic("order_Id not specified") 39 | } 40 | if req.Amount == 0 { 41 | panic("amount not specified") 42 | } 43 | if req.ProductID == 0 { 44 | panic("product_id not specified") 45 | } 46 | return &req 47 | } 48 | -------------------------------------------------------------------------------- /dtm/dtm-cases/order/conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dtm-labs/dtmcli" 7 | ) 8 | 9 | var DBConf = dtmcli.DBConf{ 10 | Driver: "mysql", 11 | Host: "en.dtm.pub", 12 | User: "dtm", 13 | Password: "passwd123dtm", 14 | Port: 3306, 15 | } 16 | 17 | var DtmServer = "http://localhost:36789/api/dtmsvr" 18 | 19 | const BusiAPI = "/api/busi" 20 | const BusiPort = 8081 21 | 22 | var BusiUrl = fmt.Sprintf("http://localhost:%d%s", BusiPort, BusiAPI) 23 | -------------------------------------------------------------------------------- /dtm/dtm-cases/order/service/api.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/dtm-labs/dtm-cases/order/common" 5 | "github.com/dtm-labs/dtm-cases/order/conf" 6 | "github.com/dtm-labs/dtm-cases/utils" 7 | "github.com/dtm-labs/dtmcli" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func AddAPIRoute(app *gin.Engine) { 12 | app.POST("/api/busi/submitOrder", utils.WrapHandler(func(c *gin.Context) interface{} { 13 | req := common.MustGetReq(c) 14 | saga := dtmcli.NewSaga(conf.DtmServer, "gid-"+req.OrderID). 15 | Add(conf.BusiUrl+"/orderCreate", conf.BusiUrl+"/orderCreateRevert", &req). 16 | Add(conf.BusiUrl+"/stockDeduct", conf.BusiUrl+"/stockDeductRevert", &req). 17 | Add(conf.BusiUrl+"/couponUse", conf.BusiUrl+"couponUseRevert", &req). 18 | Add(conf.BusiUrl+"/payCreate", conf.BusiUrl+"/payCreateRevert", &req) 19 | return saga.Submit() 20 | })) 21 | } 22 | -------------------------------------------------------------------------------- /dtm/dtm-cases/order/service/order.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "database/sql" 5 | 6 | "github.com/dtm-labs/dtm-cases/order/common" 7 | "github.com/dtm-labs/dtm-cases/utils" 8 | "github.com/dtm-labs/dtmcli/dtmimp" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func AddOrderRoute(app *gin.Engine) { 13 | app.POST("/api/busi/orderCreate", utils.WrapHandler(func(c *gin.Context) interface{} { 14 | req := common.MustGetReq(c) 15 | bb := utils.MustBarrierFrom(c) 16 | return bb.CallWithDB(common.DBGet(), func(tx *sql.Tx) error { 17 | _, err := dtmimp.DBExec(tx, 18 | "insert into ord.order1(user_id, order_id, product_id, amount, status) values(?,?,?,?,'PAYING')", 19 | req.UserID, req.OrderID, req.ProductID, req.Amount) 20 | return err 21 | }) 22 | })) 23 | app.POST("/api/busi/orderCreateRevert", utils.WrapHandler(func(c *gin.Context) interface{} { 24 | req := common.MustGetReq(c) 25 | bb := utils.MustBarrierFrom(c) 26 | return bb.CallWithDB(common.DBGet(), func(tx *sql.Tx) error { 27 | _, err := dtmimp.DBExec(tx, 28 | "update ord.order1 set status='FAILED', update_time=now() where order_id=?", 29 | req.OrderID) 30 | return err 31 | }) 32 | })) 33 | } 34 | -------------------------------------------------------------------------------- /dtm/dtm-cases/order/service/pay.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "database/sql" 5 | 6 | "github.com/dtm-labs/dtm-cases/order/common" 7 | "github.com/dtm-labs/dtm-cases/utils" 8 | "github.com/dtm-labs/dtmcli/dtmimp" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func AddPayRoute(app *gin.Engine) { 13 | app.POST("/api/busi/payCreate", utils.WrapHandler(func(c *gin.Context) interface{} { 14 | req := common.MustGetReq(c) 15 | bb := utils.MustBarrierFrom(c) 16 | return bb.CallWithDB(common.DBGet(), func(tx *sql.Tx) error { 17 | _, err := dtmimp.DBExec(tx, 18 | "insert into ord.pay(user_id, order_id, amount, status) values(?,?,?,'CREATED')", 19 | req.UserID, req.OrderID, req.Amount) 20 | return err 21 | }) 22 | })) 23 | app.POST("/api/busi/payCreateRevert", utils.WrapHandler(func(c *gin.Context) interface{} { 24 | req := common.MustGetReq(c) 25 | bb := utils.MustBarrierFrom(c) 26 | return bb.CallWithDB(common.DBGet(), func(tx *sql.Tx) error { 27 | _, err := dtmimp.DBExec(tx, 28 | "update ord.pay set status='CANCELED', update_time=now() where order_id=?", 29 | req.OrderID) 30 | return err 31 | }) 32 | })) 33 | } 34 | -------------------------------------------------------------------------------- /dtm/dtm-cases/order/service/types.go: -------------------------------------------------------------------------------- 1 | package service 2 | -------------------------------------------------------------------------------- /dtm/dtm-cases/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/dtm-labs/dtmcli" 9 | "github.com/dtm-labs/dtmcli/logger" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | // WrapHandler used by examples. much more simpler than WrapHandler2 14 | func WrapHandler(fn func(*gin.Context) interface{}) gin.HandlerFunc { 15 | return func(c *gin.Context) { 16 | began := time.Now() 17 | ret := fn(c) 18 | status, res := dtmcli.Result2HttpJSON(ret) 19 | 20 | b, _ := json.Marshal(res) 21 | if status == http.StatusOK || status == http.StatusTooEarly { 22 | logger.Infof("%2dms %d %s %s %s", time.Since(began).Milliseconds(), status, c.Request.Method, c.Request.RequestURI, string(b)) 23 | } else { 24 | logger.Errorf("%2dms %d %s %s %s", time.Since(began).Milliseconds(), status, c.Request.Method, c.Request.RequestURI, string(b)) 25 | } 26 | c.JSON(status, res) 27 | } 28 | } 29 | 30 | func MustBarrierFrom(c *gin.Context) *dtmcli.BranchBarrier { 31 | bb, err := dtmcli.BarrierFromQuery(c.Request.URL.Query()) 32 | if err != nil { 33 | panic(err) 34 | } 35 | return bb 36 | } 37 | -------------------------------------------------------------------------------- /dtm/dtm-examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | dtm-examples 17 | -------------------------------------------------------------------------------- /dtm/dtm-examples/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 DTM Development and Communities 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dtm/dtm-examples/busi/base_jrpc.go: -------------------------------------------------------------------------------- 1 | package busi 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dtm-labs/client/dtmcli/dtmimp" 7 | "github.com/dtm-labs/client/dtmcli/logger" 8 | "github.com/dtm-labs/dtm-examples/dtmutil" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | // BusiJrpcURL url prefix for busi 13 | var BusiJrpcURL = fmt.Sprintf("http://localhost:%d/api/json-rpc?method=", BusiPort) 14 | 15 | func addJrpcRoute(app *gin.Engine) { 16 | app.POST("/api/json-rpc", dtmutil.WrapHandler(func(c *gin.Context) interface{} { 17 | var data map[string]interface{} 18 | err := c.BindJSON(&data) 19 | dtmimp.E2P(err) 20 | logger.Debugf("method is: %s", data["method"]) 21 | var rerr map[string]interface{} 22 | r := MainSwitch.JrpcResult.Fetch() 23 | if r != "" { 24 | rerr = map[string]interface{}{ 25 | "code": map[string]int{ 26 | "FAILURE": dtmimp.JrpcCodeFailure, 27 | "ONGOING": dtmimp.JrpcCodeOngoing, 28 | "OTHER": -23977, 29 | }, 30 | } 31 | } 32 | return map[string]interface{}{ 33 | "jsonrpc": "2.0", 34 | "error": rerr, 35 | "id": data["id"], 36 | } 37 | })) 38 | } 39 | -------------------------------------------------------------------------------- /dtm/dtm-examples/busi/startup.go: -------------------------------------------------------------------------------- 1 | package busi 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | grpc "google.golang.org/grpc" 6 | ) 7 | 8 | // Startup startup the busi's grpc and http service 9 | func Startup() (*gin.Engine, *grpc.Server) { 10 | svr := GrpcStartup() 11 | app := BaseAppStartup() 12 | return app, svr 13 | } 14 | -------------------------------------------------------------------------------- /dtm/dtm-examples/dtmutil/consts.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmutil 8 | 9 | const ( 10 | // DefaultHTTPServer default url for http server. used by test and examples 11 | DefaultHTTPServer = "http://localhost:36789/api/dtmsvr" 12 | // DefaultJrpcServer default url for http json-rpc server. used by test and examples 13 | DefaultJrpcServer = "http://localhost:36789/api/json-rpc" 14 | // DefaultGrpcServer default url for grpc server. used by test and examples 15 | DefaultGrpcServer = "localhost:36790" 16 | ) 17 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/grpc_msg.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package examples 8 | 9 | import ( 10 | "github.com/dtm-labs/client/dtmcli/logger" 11 | dtmgrpc "github.com/dtm-labs/client/dtmgrpc" 12 | "github.com/dtm-labs/dtm-examples/busi" 13 | "github.com/dtm-labs/dtm-examples/dtmutil" 14 | "github.com/lithammer/shortuuid/v3" 15 | ) 16 | 17 | func init() { 18 | AddCommand("grpc_msg", func() string { 19 | req := &busi.ReqGrpc{Amount: 30} 20 | gid := shortuuid.New() 21 | msg := dtmgrpc.NewMsgGrpc(dtmutil.DefaultGrpcServer, gid). 22 | Add(busi.BusiGrpc+"/busi.Busi/TransOut", req). 23 | Add(busi.BusiGrpc+"/busi.Busi/TransIn", req) 24 | err := msg.Submit() 25 | logger.FatalIfError(err) 26 | return msg.Gid 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/grpc_saga_barrier.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package examples 8 | 9 | import ( 10 | "github.com/dtm-labs/client/dtmcli/logger" 11 | "github.com/dtm-labs/client/dtmgrpc" 12 | "github.com/dtm-labs/dtm-examples/busi" 13 | "github.com/dtm-labs/dtm-examples/dtmutil" 14 | "github.com/lithammer/shortuuid/v3" 15 | ) 16 | 17 | func init() { 18 | AddCommand("grpc_saga_barrier", func() string { 19 | req := &busi.ReqGrpc{Amount: 30} 20 | gid := shortuuid.New() 21 | saga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gid). 22 | Add(busi.BusiGrpc+"/busi.Busi/TransOutBSaga", busi.BusiGrpc+"/busi.Busi/TransOutRevertBSaga", req). 23 | Add(busi.BusiGrpc+"/busi.Busi/TransInBSaga", busi.BusiGrpc+"/busi.Busi/TransInRevertBSaga", req) 24 | err := saga.Submit() 25 | logger.FatalIfError(err) 26 | return saga.Gid 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/grpc_saga_other.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "github.com/dtm-labs/client/dtmcli/dtmimp" 5 | "github.com/dtm-labs/client/dtmcli/logger" 6 | dtmgrpc "github.com/dtm-labs/client/dtmgrpc" 7 | "github.com/dtm-labs/dtm-examples/busi" 8 | "github.com/dtm-labs/dtm-examples/dtmutil" 9 | "github.com/lithammer/shortuuid/v3" 10 | ) 11 | 12 | func init() { 13 | AddCommand("grpc_saga_hybrid", func() string { 14 | req := &busi.ReqGrpc{Amount: 30} 15 | gid := shortuuid.New() 16 | saga := dtmgrpc.NewSagaGrpc(dtmutil.DefaultGrpcServer, gid). 17 | Add(busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutRevert", req) 18 | saga.Steps = append(saga.Steps, map[string]string{"action": busi.Busi + "/TransIn", "compensate": busi.Busi + "/TransInRevert"}) 19 | saga.BinPayloads = append(saga.BinPayloads, dtmimp.MustMarshal(&busi.ReqGrpc{Amount: 30})) 20 | 21 | err := saga.Submit() 22 | logger.FatalIfError(err) 23 | return saga.Gid 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/grpc_xa.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package examples 8 | 9 | import ( 10 | "github.com/dtm-labs/client/dtmcli/logger" 11 | "github.com/dtm-labs/client/dtmgrpc" 12 | "github.com/dtm-labs/dtm-examples/busi" 13 | "github.com/dtm-labs/dtm-examples/dtmutil" 14 | "github.com/lithammer/shortuuid/v3" 15 | "google.golang.org/protobuf/types/known/emptypb" 16 | ) 17 | 18 | func init() { 19 | AddCommand("grpc_xa", func() string { 20 | gid := shortuuid.New() 21 | req := &busi.ReqGrpc{Amount: 30} 22 | err := dtmgrpc.XaGlobalTransaction(dtmutil.DefaultGrpcServer, gid, func(xa *dtmgrpc.XaGrpc) error { 23 | r := &emptypb.Empty{} 24 | err := xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransOutXa", r) 25 | if err != nil { 26 | return err 27 | } 28 | err = xa.CallBranch(req, busi.BusiGrpc+"/busi.Busi/TransInXa", r) 29 | return err 30 | }) 31 | logger.FatalIfError(err) 32 | return gid 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/http_more.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "github.com/dtm-labs/client/dtmcli" 5 | "github.com/dtm-labs/client/dtmcli/logger" 6 | "github.com/dtm-labs/dtm-examples/busi" 7 | "github.com/dtm-labs/dtm-examples/dtmutil" 8 | "github.com/lithammer/shortuuid/v3" 9 | ) 10 | 11 | func init() { 12 | AddCommand("http_saga_multiSource", func() string { 13 | req := &busi.ReqHTTP{Amount: 30} 14 | saga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, shortuuid.New()). 15 | Add(busi.Busi+"/SagaMultiSource", busi.Busi+"/SagaMultiSourceRevert", req) 16 | logger.Debugf("saga busi trans submit") 17 | err := saga.Submit() 18 | logger.Debugf("result gid is: %s", saga.Gid) 19 | logger.FatalIfError(err) 20 | return saga.Gid 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/http_saga_gorm_barrier.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package examples 8 | 9 | import ( 10 | "github.com/dtm-labs/client/dtmcli" 11 | "github.com/dtm-labs/client/dtmcli/logger" 12 | "github.com/dtm-labs/dtm-examples/busi" 13 | "github.com/dtm-labs/dtm-examples/dtmutil" 14 | "github.com/lithammer/shortuuid/v3" 15 | ) 16 | 17 | func init() { 18 | AddCommand("http_saga_gorm_barrier", func() string { 19 | logger.Debugf("a busi transaction begin") 20 | req := &busi.ReqHTTP{Amount: 30} 21 | saga := dtmcli.NewSaga(dtmutil.DefaultHTTPServer, shortuuid.New()). 22 | Add(busi.Busi+"/SagaBTransOutGorm", busi.Busi+"/SagaBTransOutCom", req). 23 | Add(busi.Busi+"/SagaBTransIn", busi.Busi+"/SagaBTransInCom", req) 24 | logger.Debugf("busi trans submit") 25 | err := saga.Submit() 26 | logger.FatalIfError(err) 27 | return saga.Gid 28 | }) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/http_tcc_barrier.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package examples 8 | 9 | import ( 10 | "github.com/dtm-labs/client/dtmcli" 11 | "github.com/dtm-labs/client/dtmcli/logger" 12 | "github.com/dtm-labs/dtm-examples/busi" 13 | "github.com/dtm-labs/dtm-examples/dtmutil" 14 | "github.com/go-resty/resty/v2" 15 | "github.com/lithammer/shortuuid/v3" 16 | ) 17 | 18 | func init() { 19 | AddCommand("http_tcc_barrier", func() string { 20 | logger.Debugf("tcc transaction begin") 21 | gid := shortuuid.New() 22 | err := dtmcli.TccGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) { 23 | resp, err := tcc.CallBranch(&busi.ReqHTTP{Amount: 30}, busi.Busi+"/TccBTransOutTry", 24 | busi.Busi+"/TccBTransOutConfirm", busi.Busi+"/TccBTransOutCancel") 25 | if err != nil { 26 | return resp, err 27 | } 28 | return tcc.CallBranch(&busi.ReqHTTP{Amount: 30}, busi.Busi+"/TccBTransInTry", busi.Busi+"/TccBTransInConfirm", busi.Busi+"/TccBTransInCancel") 29 | }) 30 | logger.FatalIfError(err) 31 | return gid 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/http_workflow_xa.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "database/sql" 5 | 6 | "github.com/dtm-labs/client/dtmcli" 7 | "github.com/dtm-labs/client/dtmcli/dtmimp" 8 | "github.com/dtm-labs/client/dtmcli/logger" 9 | "github.com/dtm-labs/client/workflow" 10 | "github.com/dtm-labs/dtm-examples/busi" 11 | "github.com/lithammer/shortuuid/v3" 12 | ) 13 | 14 | func init() { 15 | AddCommand("http_workflow_xa", func() string { 16 | wfName := "wf_xa" 17 | err := workflow.Register(wfName, func(wf *workflow.Workflow, data []byte) error { 18 | _, err := wf.NewBranch().DoXa(busi.BusiConf, func(db *sql.DB) ([]byte, error) { 19 | return nil, busi.SagaAdjustBalance(db, busi.TransOutUID, -30, dtmcli.ResultSuccess) 20 | }) 21 | if err != nil { 22 | return err 23 | } 24 | _, err = wf.NewBranch().DoXa(busi.BusiConf, func(db *sql.DB) ([]byte, error) { 25 | return nil, busi.SagaAdjustBalance(db, busi.TransInUID, 30, dtmcli.ResultSuccess) 26 | }) 27 | return err 28 | }) 29 | logger.FatalIfError(err) 30 | 31 | req := &busi.ReqHTTP{Amount: 30} 32 | gid := shortuuid.New() 33 | err = workflow.Execute(wfName, gid, dtmimp.MustMarshal(req)) 34 | logger.Infof("result is: %v", err) 35 | return gid 36 | }) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/http_xa.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package examples 8 | 9 | import ( 10 | "github.com/dtm-labs/client/dtmcli" 11 | "github.com/dtm-labs/client/dtmcli/logger" 12 | "github.com/dtm-labs/dtm-examples/busi" 13 | "github.com/dtm-labs/dtm-examples/dtmutil" 14 | "github.com/go-resty/resty/v2" 15 | "github.com/lithammer/shortuuid/v3" 16 | ) 17 | 18 | func init() { 19 | AddCommand("http_xa", func() string { 20 | gid := shortuuid.New() 21 | err := dtmcli.XaGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) { 22 | resp, err := xa.CallBranch(&busi.ReqHTTP{Amount: 30}, busi.Busi+"/TransOutXa") 23 | if err != nil { 24 | return resp, err 25 | } 26 | return xa.CallBranch(&busi.ReqHTTP{Amount: 30}, busi.Busi+"/TransInXa") 27 | }) 28 | logger.FatalIfError(err) 29 | return gid 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/http_xa_gorm.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package examples 8 | 9 | import ( 10 | "github.com/dtm-labs/client/dtmcli" 11 | "github.com/dtm-labs/client/dtmcli/logger" 12 | "github.com/dtm-labs/dtm-examples/busi" 13 | "github.com/dtm-labs/dtm-examples/dtmutil" 14 | "github.com/go-resty/resty/v2" 15 | "github.com/lithammer/shortuuid/v3" 16 | ) 17 | 18 | func init() { 19 | AddCommand("http_xa_gorm", func() string { 20 | gid := shortuuid.New() 21 | err := dtmcli.XaGlobalTransaction(dtmutil.DefaultHTTPServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) { 22 | resp, err := xa.CallBranch(&busi.ReqHTTP{Amount: 30}, busi.Busi+"/TransOutXaGorm") 23 | if err != nil { 24 | return resp, err 25 | } 26 | return xa.CallBranch(&busi.ReqHTTP{Amount: 30}, busi.Busi+"/TransInXa") 27 | }) 28 | logger.FatalIfError(err) 29 | return gid 30 | }) 31 | 32 | } 33 | -------------------------------------------------------------------------------- /dtm/dtm-examples/examples/utils.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "github.com/dtm-labs/client/dtmcli/dtmimp" 5 | "github.com/dtm-labs/client/dtmgrpc/dtmgimp" 6 | "github.com/dtm-labs/dtm-examples/busi" 7 | ) 8 | 9 | func MustUnmarshalReqGrpc(data []byte) *busi.ReqGrpc { 10 | var req busi.ReqGrpc 11 | dtmgimp.MustProtoUnmarshal(data, &req) 12 | return &req 13 | } 14 | 15 | func MustUnmarshalReqHTTP(data []byte) *busi.ReqHTTP { 16 | var req busi.ReqHTTP 17 | dtmimp.MustUnmarshal(data, &req) 18 | return &req 19 | } 20 | -------------------------------------------------------------------------------- /dtm/dtm-examples/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dtm-labs/dtm-examples 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/dtm-labs/client v1.17.3 7 | github.com/gin-gonic/gin v1.7.7 8 | github.com/go-redis/redis/v8 v8.11.5 9 | github.com/go-resty/resty/v2 v2.7.0 10 | github.com/go-sql-driver/mysql v1.6.0 11 | github.com/lib/pq v1.10.3 12 | github.com/lithammer/shortuuid/v3 v3.0.7 13 | go.mongodb.org/mongo-driver v1.9.1 14 | google.golang.org/grpc v1.48.0 15 | google.golang.org/protobuf v1.28.0 16 | gorm.io/driver/mysql v1.0.3 17 | gorm.io/driver/postgres v1.2.1 18 | gorm.io/gorm v1.22.2 19 | ) 20 | 21 | // replace github.com/dtm-labs/client/dtmcli => /Users/wangxi/dtm/dtmcli 22 | 23 | // replace github.com/dtm-labs/client/dtmgrpc => /Users/wangxi/dtm/dtmgrpc 24 | -------------------------------------------------------------------------------- /dtm/dtm-examples/sync-from-dtm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | rm -rf busi dtmutil 6 | cp -rf ../dtm/test/busi ./ 7 | cp -rf ../dtm/dtmutil ./ 8 | sed -i '' -e 's/dtm-labs\/dtm\/dtmutil/dtm-labs\/dtm-examples\/dtmutil/g' *.go */**.go 9 | rm -rf dtmutil/*_test.go 10 | sed -i '' -e 's/dtm-labs\/dtm\/client\/dtmcli/dtm-labs\/client\/dtmcli/g' *.go */**.go 11 | sed -i '' -e 's/dtm-labs\/dtm\/client\/dtmgrpc/dtm-labs\/client\/dtmgrpc/g' *.go */**.go 12 | sed -i '' -e 's/dtm-labs\/dtm\/client\/workflow/dtm-labs\/client\/workflow/g' *.go */**.go 13 | -------------------------------------------------------------------------------- /dtm/dtm-lab/dtmcli/dtmimp/README-cn.md: -------------------------------------------------------------------------------- 1 | ## 注意 2 | 此包带imp后缀,主要被dtm内部使用,相关接口可能会发生变更,请勿使用这里的接口 -------------------------------------------------------------------------------- /dtm/dtm-lab/dtmcli/dtmimp/README.md: -------------------------------------------------------------------------------- 1 | ## Notice 2 | Please donot use this package, and this package should only be used in dtm internally. The interfaces are not stable, and package name has postfix "imp" -------------------------------------------------------------------------------- /dtm/dtm-lab/dtmcli/dtmimp/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmimp 8 | 9 | import "database/sql" 10 | 11 | // DB inteface of dtmcli db 12 | type DB interface { 13 | Exec(query string, args ...interface{}) (sql.Result, error) 14 | QueryRow(query string, args ...interface{}) *sql.Row 15 | } 16 | 17 | // DBConf defines db config 18 | type DBConf struct { 19 | Driver string `yaml:"Driver"` 20 | Host string `yaml:"Host"` 21 | Port int64 `yaml:"Port"` 22 | User string `yaml:"User"` 23 | Password string `yaml:"Password"` 24 | Db string `yaml:"Db"` 25 | Schema string `yaml:"Schema"` 26 | } 27 | -------------------------------------------------------------------------------- /dtm/dtm-lab/dtmcli/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "github.com/dtm-labs/logger" 5 | ) 6 | 7 | var ( 8 | // WithLogger replaces default logger 9 | WithLogger = logger.WithLogger 10 | // InitLog is an initialization for a logger 11 | // level can be: debug info warn error 12 | InitLog = logger.InitLog 13 | // InitLog2 specify advanced log config 14 | InitLog2 = logger.InitLog2 15 | // Debugf log to level debug 16 | Debugf = logger.Debugf 17 | 18 | // Infof log to level info 19 | Infof = logger.Infof 20 | 21 | // Warnf log to level warn 22 | Warnf = logger.Warnf 23 | // Errorf log to level error 24 | Errorf = logger.Errorf 25 | 26 | // FatalfIf log to level error 27 | FatalfIf = logger.FatalfIf 28 | 29 | // FatalIfError if err is not nil, then log to level fatal and call os.Exit 30 | FatalIfError = logger.FatalIfError 31 | ) 32 | -------------------------------------------------------------------------------- /dtm/dtm-lab/dtmgrpc/barrier.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmgrpc 8 | 9 | import ( 10 | "context" 11 | 12 | "github.com/dtm-labs/client/dtmcli" 13 | "github.com/dtm-labs/client/dtmgrpc/dtmgimp" 14 | ) 15 | 16 | // BarrierFromGrpc generate a Barrier from grpc context 17 | func BarrierFromGrpc(ctx context.Context) (*dtmcli.BranchBarrier, error) { 18 | tb := dtmgimp.TransBaseFromGrpc(ctx) 19 | return dtmcli.BarrierFrom(tb.TransType, tb.Gid, tb.BranchID, tb.Op) 20 | } 21 | -------------------------------------------------------------------------------- /dtm/dtm-lab/dtmgrpc/dtmgimp/README-cn.md: -------------------------------------------------------------------------------- 1 | ## 注意 2 | 此包带imp后缀,主要被dtm内部使用,相关接口可能会发生变更,请勿使用这里的接口 -------------------------------------------------------------------------------- /dtm/dtm-lab/dtmgrpc/dtmgimp/README.md: -------------------------------------------------------------------------------- 1 | ## Notice 2 | Please donot use this package, and this package should only be used in dtm internally. The interfaces are not stable, and package name has postfix "imp" -------------------------------------------------------------------------------- /dtm/dtm-lab/dtmgrpc/options.go: -------------------------------------------------------------------------------- 1 | package dtmgrpc 2 | 3 | import ( 4 | "github.com/dtm-labs/client/dtmcli/dtmimp" 5 | ) 6 | 7 | // TransBaseOption setup func for TransBase 8 | type TransBaseOption func(tb *dtmimp.TransBase) 9 | 10 | // WithBranchHeaders setup TransBase.BranchHeaders 11 | func WithBranchHeaders(headers map[string]string) TransBaseOption { 12 | return func(tb *dtmimp.TransBase) { 13 | tb.BranchHeaders = headers 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /dtm/dtm-lab/dummy.go: -------------------------------------------------------------------------------- 1 | package client_test 2 | 3 | import ( 4 | _ "github.com/dtm-labs/client/dtmcli" 5 | _ "github.com/dtm-labs/client/dtmgrpc" 6 | _ "github.com/dtm-labs/client/workflow" 7 | ) 8 | -------------------------------------------------------------------------------- /dtm/dtm-lab/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dtm-labs/client 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/dtm-labs/dtmdriver v0.0.6 7 | github.com/go-redis/redis/v8 v8.11.5 8 | github.com/go-resty/resty/v2 v2.7.0 9 | go.mongodb.org/mongo-driver v1.9.1 10 | google.golang.org/grpc v1.48.0 11 | google.golang.org/protobuf v1.28.0 12 | ) 13 | 14 | require ( 15 | github.com/dtm-labs/logger v0.0.1 16 | github.com/stretchr/testify v1.8.0 // indirect 17 | ) 18 | 19 | retract v1.18.7 20 | -------------------------------------------------------------------------------- /dtm/dtm-lab/workflow/dummyReadCloser.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | ) 7 | 8 | // NewRespBodyFromBytes creates an io.ReadCloser from a byte slice 9 | // that is suitable for use as an http response body. 10 | func NewRespBodyFromBytes(body []byte) io.ReadCloser { 11 | return &dummyReadCloser{body: bytes.NewReader(body)} 12 | } 13 | 14 | type dummyReadCloser struct { 15 | body io.ReadSeeker 16 | } 17 | 18 | func (d *dummyReadCloser) Read(p []byte) (n int, err error) { 19 | return d.body.Read(p) 20 | } 21 | 22 | func (d *dummyReadCloser) Close() error { 23 | _, _ = d.body.Seek(0, io.SeekEnd) 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /dtm/dtm-lab/workflow/factory.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/dtm-labs/logger" 8 | ) 9 | 10 | type workflowFactory struct { 11 | protocol string 12 | httpDtm string 13 | httpCallback string 14 | grpcDtm string 15 | grpcCallback string 16 | handlers map[string]*wfItem 17 | } 18 | 19 | var defaultFac = workflowFactory{ 20 | handlers: map[string]*wfItem{}, 21 | } 22 | 23 | func (w *workflowFactory) execute(ctx context.Context, name string, gid string, data []byte) ([]byte, error) { 24 | handler := w.handlers[name] 25 | if handler == nil { 26 | return nil, fmt.Errorf("workflow '%s' not registered. please register at startup", name) 27 | } 28 | wf := w.newWorkflow(ctx, name, gid, data) 29 | for _, fn := range handler.custom { 30 | fn(wf) 31 | } 32 | return wf.process(handler.fn, data) 33 | } 34 | 35 | func (w *workflowFactory) register(name string, handler WfFunc2, custom ...func(wf *Workflow)) error { 36 | e := w.handlers[name] 37 | if e != nil { 38 | return fmt.Errorf("a handler already exists for %s", name) 39 | } 40 | logger.Debugf("workflow '%s' registered.", name) 41 | w.handlers[name] = &wfItem{ 42 | fn: handler, 43 | custom: custom, 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /dtm/dtm-lab/workflow/server.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/dtm-labs/client/dtmcli/dtmimp" 7 | "github.com/dtm-labs/client/dtmgrpc" 8 | "github.com/dtm-labs/client/dtmgrpc/dtmgimp" 9 | "github.com/dtm-labs/client/workflow/wfpb" 10 | "google.golang.org/grpc/codes" 11 | "google.golang.org/grpc/status" 12 | "google.golang.org/protobuf/types/known/emptypb" 13 | ) 14 | 15 | type workflowServer struct { 16 | wfpb.UnimplementedWorkflowServer 17 | } 18 | 19 | func (s *workflowServer) Execute(ctx context.Context, wd *wfpb.WorkflowData) (*emptypb.Empty, error) { 20 | if defaultFac.protocol != dtmimp.ProtocolGRPC { 21 | return nil, status.Errorf(codes.Internal, "workflow server not inited. please call workflow.InitGrpc first") 22 | } 23 | tb := dtmgimp.TransBaseFromGrpc(ctx) 24 | _, err := defaultFac.execute(ctx, tb.Op, tb.Gid, wd.Data) 25 | return &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(err) 26 | } 27 | -------------------------------------------------------------------------------- /dtm/dtm-lab/workflow/wfpb/wf.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "./wfpb"; 4 | import "google/protobuf/empty.proto"; 5 | 6 | package workflow; 7 | 8 | // The Workflow service definition. 9 | service Workflow { 10 | rpc Execute(WorkflowData) returns (google.protobuf.Empty) {} 11 | } 12 | 13 | message WorkflowData { 14 | bytes Data = 1; 15 | } 16 | -------------------------------------------------------------------------------- /dtm/dtm/.gitignore: -------------------------------------------------------------------------------- 1 | conf.yml 2 | *.out 3 | *.log 4 | # dist 5 | .idea/** 6 | .vscode 7 | default.etcd 8 | */**/*.bolt 9 | bench/bench 10 | helper/bench/bench 11 | helper/qs/qs 12 | # Output file of unit test coverage 13 | coverage.* 14 | profile.* 15 | test.sh 16 | dtm 17 | dtm-* 18 | dtm.* 19 | cache 20 | 21 | -------------------------------------------------------------------------------- /dtm/dtm/Makefile: -------------------------------------------------------------------------------- 1 | # dev env https://www.dtm.pub/other/develop.html 2 | all: fmt lint test_redis 3 | .PHONY: all 4 | 5 | fmt: 6 | @gofmt -s -w ./ 7 | 8 | lint: 9 | revive -config revive.toml ./... 10 | 11 | .PHONY: test 12 | test: 13 | @go test ./... 14 | 15 | test_redis: 16 | TEST_STORE=redis go test ./... 17 | 18 | test_all: 19 | TEST_STORE=redis go test ./... 20 | TEST_STORE=boltdb go test ./... 21 | TEST_STORE=mysql go test ./... 22 | TEST_STORE=postgres go test ./... 23 | 24 | cover_test: 25 | ./helper/test-cover.sh 26 | 27 | -------------------------------------------------------------------------------- /dtm/dtm/admin/.env: -------------------------------------------------------------------------------- 1 | VITE_ADMIN_VERSION="v0.0.0-dev" 2 | -------------------------------------------------------------------------------- /dtm/dtm/admin/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /dtm/dtm/admin/README.md: -------------------------------------------------------------------------------- 1 | # DTM-Admin 2 | -------------------------------------------------------------------------------- /dtm/dtm/admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Dtm 9 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /dtm/dtm/admin/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | } -------------------------------------------------------------------------------- /dtm/dtm/admin/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/dtm/dtm/admin/public/favicon.ico -------------------------------------------------------------------------------- /dtm/dtm/admin/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/assets/css/index.css: -------------------------------------------------------------------------------- 1 | /* @tailwind base; */ 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/icons/readme.md: -------------------------------------------------------------------------------- 1 | # ICon Component 2 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from '/@/router/index' 4 | import { pinia } from '/@/store' 5 | import { useLayoutStore } from '/@/store/modules/layout' 6 | import '/@/permission' 7 | 8 | import 'ant-design-vue/dist/antd.css' 9 | import '/@/assets/css/index.css' 10 | import 'virtual:svg-icons-register' 11 | 12 | const app = createApp(App) 13 | app.use(router) 14 | app.use(pinia) 15 | app.mount('#app') 16 | 17 | window.onunhandledrejection = (ev: PromiseRejectionEvent) => { 18 | showAlert(ev.reason.stack || ev.reason.message) 19 | } 20 | window.onerror = err => { 21 | if (typeof err === 'string') { 22 | return showAlert(err) 23 | } 24 | showAlert(JSON.stringify(err)) 25 | } 26 | 27 | function showAlert(msg: string) { 28 | const layout = useLayoutStore() 29 | if (!layout.globalError) { 30 | layout.setGlobalError(msg) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/permission.ts: -------------------------------------------------------------------------------- 1 | import router from '/@/router' 2 | import { configure, start, done } from 'nprogress' 3 | import { useLayoutStore } from './store/modules/layout' 4 | 5 | configure({ showSpinner: false }) 6 | 7 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 8 | const defaultRoutePath = '/' 9 | 10 | router.beforeEach((to) => { 11 | start() 12 | 13 | const { getMenubar, concatAllowRoutes } = useLayoutStore() 14 | 15 | if (getMenubar.menuList.length === 0) { 16 | concatAllowRoutes() 17 | 18 | return to.fullPath 19 | } 20 | }) 21 | 22 | router.afterEach(() => { 23 | done() 24 | }) 25 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/router/asyncRouter.ts: -------------------------------------------------------------------------------- 1 | const modules = import.meta.glob('../views/**/**.vue') 2 | const components: IObject<() => Promise> = { 3 | LayoutHeader: (() => import('/@/layout/index.vue')) as unknown as () => Promise 4 | } 5 | 6 | Object.keys(modules).forEach(key => { 7 | const nameMatch = key.match(/^\.\.\/views\/(.+)\.vue/) 8 | if (!nameMatch) return 9 | if (nameMatch[1].includes('_Components')) return 10 | const indexMatch = nameMatch[1].match(/(.*)\/Index$/i) 11 | let name = indexMatch ? indexMatch[1] : nameMatch[1]; 12 | [name] = name.split('/').splice(-1) 13 | components[name] = modules[key] as () => Promise 14 | }) 15 | 16 | export { 17 | components 18 | } 19 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia' 2 | export const pinia = createPinia() 3 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/type/index.d.ts: -------------------------------------------------------------------------------- 1 | export { } 2 | declare global { 3 | interface IObject { 4 | [index: string]: T 5 | } 6 | interface ImportMetaEnv { 7 | VITE_APP_TITLE: string 8 | VITE_PORT: number 9 | VITE_PROXY: string 10 | VITE_ADMIN_VERSION: string 11 | } 12 | interface ITable { 13 | data: Array 14 | next_position: number, 15 | size: number 16 | } 17 | interface Window { 18 | basePath: string; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/type/shim.vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { defineComponent } from 'vue' 3 | const Component: ReturnType 4 | export default Component 5 | } 6 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/type/store/layout.ts: -------------------------------------------------------------------------------- 1 | export interface IMenubar { 2 | menuList: Array 3 | } 4 | 5 | export interface ILayout { 6 | menubar: IMenubar 7 | status: IStatus 8 | dtmVersion: string 9 | globalError: string 10 | } 11 | 12 | export interface IStatus { 13 | isLoading: boolean 14 | } 15 | 16 | export interface IMenubarList { 17 | parentId?: number | string 18 | id?: number | string 19 | name: string 20 | path: string 21 | redirect?: string 22 | meta: { 23 | icon?: string 24 | title: string 25 | permission?: string[] 26 | activeMenu?: string 27 | hidden?: boolean 28 | alwaysShow?: boolean 29 | } 30 | component: (() => Promise) | string 31 | children?: Array 32 | } 33 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/utils/request.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | const request = axios.create({ 4 | timeout: 60000 5 | }) 6 | 7 | export default request 8 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/utils/util.ts: -------------------------------------------------------------------------------- 1 | import { useRoute } from 'vue-router' 2 | import { IMenubarList } from '../type/store/layout' 3 | 4 | export const findCurrentMenubar = (menuList: IMenubarList[], root?: boolean) => { 5 | const route = useRoute() 6 | let arr: IMenubarList[] | IMenubarList = [] 7 | for (let i = 0; i < menuList.length; i++) { 8 | const v = menuList[i] 9 | const usePath = v.meta.activeMenu || v.redirect || v.path 10 | const pos = usePath.lastIndexOf('/') 11 | const rootPath = pos == 0 ? usePath : usePath.substring(0, pos) 12 | if (route.path.indexOf(rootPath) !== -1) { 13 | if (!root) { 14 | arr = v.children as IMenubarList[] 15 | } else { 16 | arr = v 17 | } 18 | break 19 | } 20 | } 21 | 22 | return arr 23 | } 24 | 25 | export const sleep = async(ms: number) => { 26 | return new Promise(resolve => setTimeout(resolve, ms)) 27 | } -------------------------------------------------------------------------------- /dtm/dtm/admin/src/views/Dashboard/GlobalTransactions/UnfinishedTransactions.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /dtm/dtm/admin/src/views/Dashboard/Nodes/LivingNodes.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /dtm/dtm/admin/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {} 5 | }, 6 | plugins: [] 7 | } 8 | -------------------------------------------------------------------------------- /dtm/dtm/admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "baseUrl": ".", 13 | "paths": { 14 | "/@/*": ["src/*"], 15 | }, 16 | "lib": ["esnext", "dom"], 17 | "types": ["vite/client", "node"] 18 | }, 19 | "include": ["**/*.ts", "**/*.d.ts", "**/*.tsx", "**/*.vue"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /dtm/dtm/charts/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /dtm/dtm/charts/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: dtm 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | # It is recommended to use it with quotes. 24 | appVersion: "1.12.2" 25 | -------------------------------------------------------------------------------- /dtm/dtm/charts/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "dtm.fullname" . }}-conf 5 | labels: 6 | {{- include "dtm.labels" . | nindent 4 }} 7 | data: 8 | config.yaml: |- 9 | {{- .Values.configuration | nindent 4 }} -------------------------------------------------------------------------------- /dtm/dtm/charts/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "dtm.fullname" . }} 6 | labels: 7 | {{- include "dtm.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "dtm.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /dtm/dtm/charts/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "dtm.fullname" . }} 5 | labels: 6 | {{- include "dtm.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.ports.http }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | - port: {{ .Values.service.ports.grpc }} 15 | targetPort: grpc 16 | protocol: TCP 17 | name: grpc 18 | selector: 19 | {{- include "dtm.selectorLabels" . | nindent 4 }} 20 | -------------------------------------------------------------------------------- /dtm/dtm/charts/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "dtm.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "dtm.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "dtm.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /dtm/dtm/client/dtmcli/cover_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmcli 8 | 9 | import ( 10 | "net/url" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestQuery(t *testing.T) { 17 | qs, err := url.ParseQuery("a=b") 18 | assert.Nil(t, err) 19 | _, err = XaFromQuery(qs) 20 | assert.Error(t, err) 21 | _, err = TccFromQuery(qs) 22 | assert.Error(t, err) 23 | _, err = BarrierFromQuery(qs) 24 | assert.Error(t, err) 25 | } 26 | -------------------------------------------------------------------------------- /dtm/dtm/client/dtmcli/dtmimp/README-cn.md: -------------------------------------------------------------------------------- 1 | ## 注意 2 | 此包带imp后缀,主要被dtm内部使用,相关接口可能会发生变更,请勿使用这里的接口 -------------------------------------------------------------------------------- /dtm/dtm/client/dtmcli/dtmimp/README.md: -------------------------------------------------------------------------------- 1 | ## Notice 2 | Please donot use this package, and this package should only be used in dtm internally. The interfaces are not stable, and package name has postfix "imp" -------------------------------------------------------------------------------- /dtm/dtm/client/dtmcli/dtmimp/db_special_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmimp 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestDBSpecial(t *testing.T) { 16 | old := currentDBType 17 | assert.Error(t, CatchP(func() { 18 | SetCurrentDBType("no-driver") 19 | })) 20 | SetCurrentDBType(DBTypeMysql) 21 | sp := GetDBSpecial(DBTypeMysql) 22 | 23 | assert.Equal(t, "? ?", sp.GetPlaceHoldSQL("? ?")) 24 | assert.Equal(t, "xa start 'xa1'", sp.GetXaSQL("start", "xa1")) 25 | assert.Equal(t, "insert ignore into a(f) values(?)", sp.GetInsertIgnoreTemplate("a(f) values(?)", "c")) 26 | SetCurrentDBType(DBTypePostgres) 27 | sp = GetDBSpecial(DBTypePostgres) 28 | assert.Equal(t, "$1 $2", sp.GetPlaceHoldSQL("? ?")) 29 | assert.Equal(t, "begin", sp.GetXaSQL("start", "xa1")) 30 | assert.Equal(t, "insert into a(f) values(?) on conflict ON CONSTRAINT c do nothing", sp.GetInsertIgnoreTemplate("a(f) values(?)", "c")) 31 | SetCurrentDBType(old) 32 | } 33 | -------------------------------------------------------------------------------- /dtm/dtm/client/dtmcli/dtmimp/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmimp 8 | 9 | import "database/sql" 10 | 11 | // DB interface of dtmcli db 12 | type DB interface { 13 | Exec(query string, args ...interface{}) (sql.Result, error) 14 | QueryRow(query string, args ...interface{}) *sql.Row 15 | } 16 | 17 | // DBConf defines db config 18 | type DBConf struct { 19 | Driver string `yaml:"Driver"` 20 | Host string `yaml:"Host"` 21 | Port int64 `yaml:"Port"` 22 | User string `yaml:"User"` 23 | Password string `yaml:"Password"` 24 | Db string `yaml:"Db"` 25 | Schema string `yaml:"Schema"` 26 | } 27 | -------------------------------------------------------------------------------- /dtm/dtm/client/dtmcli/dtmimp/types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmimp 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestTypes(t *testing.T) { 16 | err := CatchP(func() { 17 | idGen := BranchIDGen{BranchID: "12345678901234567890123"} 18 | idGen.NewSubBranchID() 19 | }) 20 | assert.Error(t, err) 21 | err = CatchP(func() { 22 | idGen := BranchIDGen{subBranchID: 99} 23 | idGen.NewSubBranchID() 24 | }) 25 | assert.Error(t, err) 26 | } 27 | -------------------------------------------------------------------------------- /dtm/dtm/client/dtmcli/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "github.com/dtm-labs/logger" 5 | ) 6 | 7 | var ( 8 | // WithLogger replaces default logger 9 | WithLogger = logger.WithLogger 10 | // InitLog is an initialization for a logger 11 | // level can be: debug info warn error 12 | InitLog = logger.InitLog 13 | // InitLog2 specify advanced log config 14 | InitLog2 = logger.InitLog2 15 | // Debugf log to level debug 16 | Debugf = logger.Debugf 17 | 18 | // Infof log to level info 19 | Infof = logger.Infof 20 | 21 | // Warnf log to level warn 22 | Warnf = logger.Warnf 23 | // Errorf log to level error 24 | Errorf = logger.Errorf 25 | 26 | // FatalfIf log to level error 27 | FatalfIf = logger.FatalfIf 28 | 29 | // FatalIfError if err is not nil, then log to level fatal and call os.Exit 30 | FatalIfError = logger.FatalIfError 31 | ) 32 | -------------------------------------------------------------------------------- /dtm/dtm/client/dtmcli/types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmcli 8 | 9 | import ( 10 | "net/url" 11 | "testing" 12 | 13 | "github.com/dtm-labs/dtm/client/dtmcli/dtmimp" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestTypes(t *testing.T) { 18 | err := dtmimp.CatchP(func() { 19 | MustGenGid("http://localhost:36789/api/no") 20 | }) 21 | assert.Error(t, err) 22 | assert.Error(t, err) 23 | _, err = BarrierFromQuery(url.Values{}) 24 | assert.Error(t, err) 25 | 26 | } 27 | 28 | func TestXaSqlTimeout(t *testing.T) { 29 | SetBarrierTableName(dtmimp.BarrierTableName) // just cover this func 30 | } 31 | -------------------------------------------------------------------------------- /dtm/dtm/client/dtmgrpc/barrier.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmgrpc 8 | 9 | import ( 10 | "context" 11 | 12 | "github.com/dtm-labs/dtm/client/dtmcli" 13 | "github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp" 14 | ) 15 | 16 | // BarrierFromGrpc generate a Barrier from grpc context 17 | func BarrierFromGrpc(ctx context.Context) (*dtmcli.BranchBarrier, error) { 18 | tb := dtmgimp.TransBaseFromGrpc(ctx) 19 | return dtmcli.BarrierFrom(tb.TransType, tb.Gid, tb.BranchID, tb.Op) 20 | } 21 | -------------------------------------------------------------------------------- /dtm/dtm/client/dtmgrpc/dtmgimp/README-cn.md: -------------------------------------------------------------------------------- 1 | ## 注意 2 | 此包带imp后缀,主要被dtm内部使用,相关接口可能会发生变更,请勿使用这里的接口 -------------------------------------------------------------------------------- /dtm/dtm/client/dtmgrpc/dtmgimp/README.md: -------------------------------------------------------------------------------- 1 | ## Notice 2 | Please donot use this package, and this package should only be used in dtm internally. The interfaces are not stable, and package name has postfix "imp" -------------------------------------------------------------------------------- /dtm/dtm/client/dtmgrpc/options.go: -------------------------------------------------------------------------------- 1 | package dtmgrpc 2 | 3 | import ( 4 | "github.com/dtm-labs/dtm/client/dtmcli/dtmimp" 5 | ) 6 | 7 | // TransBaseOption setup func for TransBase 8 | type TransBaseOption func(tb *dtmimp.TransBase) 9 | 10 | // WithBranchHeaders setup TransBase.BranchHeaders 11 | func WithBranchHeaders(headers map[string]string) TransBaseOption { 12 | return func(tb *dtmimp.TransBase) { 13 | tb.BranchHeaders = headers 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /dtm/dtm/client/dtmgrpc/type_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmgrpc 8 | 9 | import ( 10 | "context" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestType(t *testing.T) { 17 | _, err := BarrierFromGrpc(context.Background()) 18 | assert.Error(t, err) 19 | 20 | _, err = TccFromGrpc(context.Background()) 21 | assert.Error(t, err) 22 | 23 | err = UseDriver("default") 24 | assert.Nil(t, err) 25 | } 26 | -------------------------------------------------------------------------------- /dtm/dtm/client/workflow/dummyReadCloser.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | ) 7 | 8 | // NewRespBodyFromBytes creates an io.ReadCloser from a byte slice 9 | // that is suitable for use as an http response body. 10 | func NewRespBodyFromBytes(body []byte) io.ReadCloser { 11 | return &dummyReadCloser{body: bytes.NewReader(body)} 12 | } 13 | 14 | type dummyReadCloser struct { 15 | body io.ReadSeeker 16 | } 17 | 18 | func (d *dummyReadCloser) Read(p []byte) (n int, err error) { 19 | return d.body.Read(p) 20 | } 21 | 22 | func (d *dummyReadCloser) Close() error { 23 | _, _ = d.body.Seek(0, io.SeekEnd) 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /dtm/dtm/client/workflow/factory.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/dtm-labs/logger" 8 | ) 9 | 10 | type workflowFactory struct { 11 | protocol string 12 | httpDtm string 13 | httpCallback string 14 | grpcDtm string 15 | grpcCallback string 16 | handlers map[string]*wfItem 17 | } 18 | 19 | var defaultFac = workflowFactory{ 20 | handlers: map[string]*wfItem{}, 21 | } 22 | 23 | func (w *workflowFactory) execute(ctx context.Context, name string, gid string, data []byte) ([]byte, error) { 24 | handler := w.handlers[name] 25 | if handler == nil { 26 | return nil, fmt.Errorf("workflow '%s' not registered. please register at startup", name) 27 | } 28 | wf := w.newWorkflow(ctx, name, gid, data) 29 | for _, fn := range handler.custom { 30 | fn(wf) 31 | } 32 | return wf.process(handler.fn, data) 33 | } 34 | 35 | func (w *workflowFactory) register(name string, handler WfFunc2, custom ...func(wf *Workflow)) error { 36 | e := w.handlers[name] 37 | if e != nil { 38 | return fmt.Errorf("a handler already exists for %s", name) 39 | } 40 | logger.Debugf("workflow '%s' registered.", name) 41 | w.handlers[name] = &wfItem{ 42 | fn: handler, 43 | custom: custom, 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /dtm/dtm/client/workflow/server.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/dtm-labs/dtm/client/dtmcli/dtmimp" 7 | "github.com/dtm-labs/dtm/client/dtmgrpc" 8 | "github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp" 9 | "github.com/dtm-labs/dtm/client/workflow/wfpb" 10 | "google.golang.org/grpc/codes" 11 | "google.golang.org/grpc/status" 12 | "google.golang.org/protobuf/types/known/emptypb" 13 | ) 14 | 15 | type workflowServer struct { 16 | wfpb.UnimplementedWorkflowServer 17 | } 18 | 19 | func (s *workflowServer) Execute(ctx context.Context, wd *wfpb.WorkflowData) (*emptypb.Empty, error) { 20 | if defaultFac.protocol != dtmimp.ProtocolGRPC { 21 | return nil, status.Errorf(codes.Internal, "workflow server not inited. please call workflow.InitGrpc first") 22 | } 23 | tb := dtmgimp.TransBaseFromGrpc(ctx) 24 | _, err := defaultFac.execute(ctx, tb.Op, tb.Gid, wd.Data) 25 | return &emptypb.Empty{}, dtmgrpc.DtmError2GrpcError(err) 26 | } 27 | -------------------------------------------------------------------------------- /dtm/dtm/client/workflow/wfpb/wf.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "./wfpb"; 4 | import "google/protobuf/empty.proto"; 5 | 6 | package workflow; 7 | 8 | // The Workflow service definition. 9 | service Workflow { 10 | rpc Execute(WorkflowData) returns (google.protobuf.Empty) {} 11 | } 12 | 13 | message WorkflowData { 14 | bytes Data = 1; 15 | } 16 | -------------------------------------------------------------------------------- /dtm/dtm/client/workflow/workflow_test.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/dtm-labs/dtm/client/dtmcli/dtmimp" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestAbnormal(t *testing.T) { 12 | fname := dtmimp.GetFuncName() 13 | _, err := defaultFac.execute(context.Background(), fname, fname, nil) 14 | assert.Error(t, err) 15 | 16 | err = defaultFac.register(fname, func(wf *Workflow, data []byte) ([]byte, error) { return nil, nil }) 17 | assert.Nil(t, err) 18 | err = defaultFac.register(fname, nil) 19 | assert.Error(t, err) 20 | 21 | ws := &workflowServer{} 22 | _, err = ws.Execute(context.Background(), nil) 23 | assert.Contains(t, err.Error(), "call workflow.InitGrpc first") 24 | } 25 | -------------------------------------------------------------------------------- /dtm/dtm/dtmsvr/microservices/drivers.go: -------------------------------------------------------------------------------- 1 | package microservices 2 | 3 | import ( 4 | // load the microserver drivers 5 | _ "github.com/dtm-labs/dtmdriver-dapr" 6 | _ "github.com/dtm-labs/dtmdriver-ego" 7 | _ "github.com/dtm-labs/dtmdriver-gozero" 8 | _ "github.com/dtm-labs/dtmdriver-kratos" 9 | _ "github.com/dtm-labs/dtmdriver-polaris" 10 | _ "github.com/dtm-labs/dtmdriver-springcloud" 11 | _ "github.com/zhufuyi/dtmdriver-sponge" 12 | ) 13 | -------------------------------------------------------------------------------- /dtm/dtm/dtmsvr/storage/registry/factory.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/dtm-labs/dtm/dtmsvr/storage" 7 | ) 8 | 9 | // SingletonFactory is the factory to build store in SINGLETON pattern. 10 | type SingletonFactory struct { 11 | once sync.Once 12 | 13 | store storage.Store 14 | 15 | creatorFunction func() storage.Store 16 | } 17 | 18 | // GetStorage implement the StorageFactory.GetStorage 19 | func (f *SingletonFactory) GetStorage() storage.Store { 20 | f.once.Do(func() { 21 | f.store = f.creatorFunction() 22 | }) 23 | 24 | return f.store 25 | } 26 | -------------------------------------------------------------------------------- /dtm/dtm/dtmutil/consts.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package dtmutil 8 | 9 | const ( 10 | // DefaultHTTPServer default url for http server. used by test and examples 11 | DefaultHTTPServer = "http://localhost:36789/api/dtmsvr" 12 | // DefaultJrpcServer default url for http json-rpc server. used by test and examples 13 | DefaultJrpcServer = "http://localhost:36789/api/json-rpc" 14 | // DefaultGrpcServer default url for grpc server. used by test and examples 15 | DefaultGrpcServer = "localhost:36790" 16 | ) 17 | -------------------------------------------------------------------------------- /dtm/dtm/helper/.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # .goreleaser.yml 2 | project_name: dtm 3 | builds: 4 | - id: dtm_amd64 5 | env: [CGO_ENABLED=0] 6 | goos: 7 | - linux 8 | - windows 9 | - darwin 10 | goarch: 11 | - amd64 12 | dir: . 13 | main: main.go 14 | ldflags: 15 | - -s -w -X main.Version=v{{.Version}} 16 | - id: dtm_arm64 17 | env: [CGO_ENABLED=0] 18 | goos: 19 | - darwin 20 | goarch: 21 | - arm64 22 | dir: . 23 | main: main.go 24 | ldflags: 25 | - -s -w -X main.Version=v{{.Version}} 26 | -------------------------------------------------------------------------------- /dtm/dtm/helper/Dockerfile-release: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | # FROM node:14.19-alpine as builder1 3 | # ARG RELEASE_VERSION 4 | # WORKDIR /app/dtm 5 | # COPY . . 6 | # RUN cd admin && yarn && VITE_ADMIN_VERSION=$RELEASE_VERSION yarn build 7 | 8 | FROM --platform=amd64 node as builder2 9 | ARG TARGETARCH 10 | ARG TARGETOS 11 | ARG RELEASE_VERSION 12 | WORKDIR /app/dtm 13 | COPY . . 14 | RUN cd admin && yarn && VITE_ADMIN_VERSION=$RELEASE_VERSION yarn build 15 | 16 | FROM --platform=$TARGETPLATFORM golang:1.18-alpine as builder1 17 | ARG TARGETARCH 18 | ARG TARGETOS 19 | ARG RELEASE_VERSION 20 | WORKDIR /app/dtm 21 | # RUN go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct 22 | COPY . . 23 | COPY --from=builder2 /app/dtm/admin/dist /app/dtm/admin/dist 24 | RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-s -w -X main.Version=$RELEASE_VERSION" 25 | 26 | FROM --platform=$TARGETPLATFORM alpine 27 | COPY --from=builder1 /app/dtm/dtm /app/dtm/ 28 | WORKDIR /app/dtm 29 | EXPOSE 8080 30 | ENTRYPOINT ["/app/dtm/dtm"] 31 | -------------------------------------------------------------------------------- /dtm/dtm/helper/bench/Makefile: -------------------------------------------------------------------------------- 1 | # All targets. 2 | default: bench 3 | 4 | # configure these paths according to you system 5 | bench: /usr/local/bin/go /etc/redis/redis.conf /usr/local/bin/docker-compose main.go 6 | rm -f ../conf.sample.yml 7 | go build -o bench 8 | 9 | go: /usr/local/bin/go 10 | 11 | redis: /etc/redis/redis.conf 12 | 13 | mysql: /usr/local/bin/docker-compose 14 | 15 | /usr/local/bin/go: 16 | wget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz 17 | rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.1.linux-amd64.tar.gz && cp -f /usr/local/go/bin/go /usr/local/bin/go && rm go1.* 18 | 19 | /etc/redis/redis.conf: 20 | apt update 21 | apt install -y redis redis-tools 22 | 23 | /usr/local/bin/docker-compose: 24 | apt update 25 | apt install -y sysbench apache2-utils mysql-client-core-8.0 26 | curl -fsSL https://get.docker.com | sh 27 | curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 28 | chmod +x /usr/local/bin/docker-compose 29 | cd .. && docker-compose -f helper/compose.mysql.yml up -d && cd bench 30 | -------------------------------------------------------------------------------- /dtm/dtm/helper/bench/prepare.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | apt update 3 | apt install -y git 4 | git clone https://github.com/dtm-labs/dtm.git && cd dtm && git checkout alpha && cd bench && make 5 | 6 | 7 | echo 'all prepared. you shoud run following commands to test in different terminal' 8 | echo 9 | echo 'cd dtm && go run helper/bench/main.go redis|boltdb|db' 10 | echo 'cd dtm && ./helper/bench/test-redis|boltdb|mysql.sh' 11 | -------------------------------------------------------------------------------- /dtm/dtm/helper/bench/setup-redis6.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | apt update 3 | apt install -y software-properties-common 4 | add-apt-repository -y ppa:redislabs/redis 5 | apt install -y redis redis-tools 6 | 7 | -------------------------------------------------------------------------------- /dtm/dtm/helper/bench/setup.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | # install all commands needed 4 | 5 | apt update 6 | apt install -y sysbench apache2-utils mysql-client-core-8.0 redis redis-tools 7 | 8 | # install docker and docker-compose 9 | curl -fsSL https://get.docker.com -o get-docker.sh 10 | sh get-docker.sh 11 | curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 12 | chmod +x /usr/local/bin/docker-compose 13 | 14 | # install go 15 | wget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz 16 | rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.1.linux-amd64.tar.gz && cp -f /usr/local/go/bin/go /usr/local/bin/go 17 | -------------------------------------------------------------------------------- /dtm/dtm/helper/bench/test-boltdb.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | set -x 4 | 5 | ab -n 50000 -c 10 "http://127.0.0.1:8083/api/busi_bench/benchEmptyUrl" 6 | -------------------------------------------------------------------------------- /dtm/dtm/helper/bench/test-flash-sales.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | set -x 4 | 5 | export LOG_LEVEL=fatal 6 | export STORE_DRIVER=redis 7 | export STORE_HOST=localhost 8 | export STORE_PORT=6379 9 | export BUSI_REDIS=localhost:6379 10 | ./bench redis & 11 | echo 'sleeping 3s for dtm bench to run up.' && sleep 3 12 | curl "http://127.0.0.1:8083/api/busi_bench/benchFlashSalesReset" 13 | ab -n 300000 -c 20 "http://127.0.0.1:8083/api/busi_bench/benchFlashSales" 14 | pkill bench 15 | -------------------------------------------------------------------------------- /dtm/dtm/helper/bench/test-redis.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | set -x 4 | 5 | export LOG_LEVEL=warn 6 | export STORE_DRIVER=redis 7 | export STORE_HOST=localhost 8 | export STORE_PORT=6379 9 | cd .. && bench/bench redis & 10 | echo 'sleeping 3s for dtm bench to run up.' && sleep 3 11 | ab -n 1000000 -c 10 "http://127.0.0.1:8083/api/busi_bench/benchEmptyUrl" 12 | pkill bench 13 | 14 | redis-benchmark -n 300000 SET 'abcdefg' 'ddddddd' 15 | 16 | redis-benchmark -n 300000 EVAL "redis.call('SET', 'abcdedf', 'ddddddd')" 0 17 | 18 | redis-benchmark -n 300000 EVAL "redis.call('SET', KEYS[1], ARGV[1])" 1 'aaaaaaaaa' 'bbbbbbbbbb' 19 | 20 | redis-benchmark -n 3000000 -P 50 SET 'abcdefg' 'ddddddd' 21 | 22 | redis-benchmark -n 300000 EVAL "for k=1, 10 do; redis.call('SET', KEYS[1], ARGV[1]);end" 1 'aaaaaaaaa' 'bbbbbbbbbb' 23 | 24 | redis-benchmark -n 300000 -P 50 EVAL "redis.call('SET', KEYS[1], ARGV[1])" 1 'aaaaaaaaa' 'bbbbbbbbbb' 25 | 26 | redis-benchmark -n 300000 EVAL "for k=1,10 do;local c = cjson.decode(ARGV[1]);end" 1 'aaaaaaaaa' '{"aaaaa":"bbbbb","b":1,"t":"2012-01-01 14:00:00"}' 27 | 28 | -------------------------------------------------------------------------------- /dtm/dtm/helper/compose.store.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | mysql: 4 | image: 'mysql:5.7' 5 | volumes: 6 | - /etc/localtime:/etc/localtime:ro 7 | environment: 8 | MYSQL_ALLOW_EMPTY_PASSWORD: 1 9 | command: 10 | [ 11 | '--character-set-server=utf8mb4', 12 | '--collation-server=utf8mb4_unicode_ci', 13 | ] 14 | ports: 15 | - '3306:3306' 16 | postgres: 17 | image: 'postgres:13' 18 | command: postgres --max_prepared_transactions=1000 19 | volumes: 20 | - /etc/localtime:/etc/localtime:ro 21 | environment: 22 | POSTGRES_PASSWORD: mysecretpassword 23 | POSTGRES_DB: dtm 24 | 25 | ports: 26 | - '5432:5432' 27 | redis: 28 | image: 'redis' 29 | volumes: 30 | - /etc/localtime:/etc/localtime:ro 31 | ports: 32 | - '6379:6379' 33 | mongo: 34 | image: yedf/mongo-rs 35 | volumes: 36 | - /etc/localtime:/etc/localtime:ro 37 | ports: 38 | - '27017:27017' 39 | sqlserver2019: 40 | image: mcr.microsoft.com/mssql/server:2019-latest 41 | volumes: 42 | - /etc/localtime:/etc/localtime:ro 43 | ports: 44 | - 1433:1433 45 | environment: 46 | - ACCEPT_EULA=Y 47 | - MSSQL_SA_PASSWORD=p@ssw0rd 48 | -------------------------------------------------------------------------------- /dtm/dtm/helper/golint.sh: -------------------------------------------------------------------------------- 1 | set -x 2 | 3 | go install github.com/mgechev/revive@latest && revive -config revive.toml ./... 4 | -------------------------------------------------------------------------------- /dtm/dtm/helper/sync-client.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | set -x 3 | ver=$1 4 | if [ x$ver == x ]; then 5 | echo please specify you version like vx.x.x; 6 | exit 1; 7 | fi 8 | 9 | if [ ${ver:0:1} != v ]; then 10 | echo please specify you version like vx.x.x; 11 | exit 1; 12 | fi 13 | 14 | cd ../client 15 | cp -rf ../dtm/client/* ./ 16 | sed -i '' -e 's/dtm-labs\/dtm\//dtm-labs\//g' */*.go */*/*.go 17 | 18 | rm -rf */*_test.go */*/*_test.go */*log */*/*log 19 | go mod tidy 20 | go build || exit 1 21 | 22 | git add . 23 | git commit -m"update from dtm to version $ver" 24 | git push 25 | git tag $ver 26 | git push --tags 27 | 28 | cd ../quick-start-sample 29 | 30 | go get -u github.com/dtm-labs/client@$ver 31 | go mod tidy 32 | go build || exit 1 33 | git add . 34 | git commit -m"update from dtm to version $ver" 35 | git push 36 | 37 | -------------------------------------------------------------------------------- /dtm/dtm/helper/test-cover.sh: -------------------------------------------------------------------------------- 1 | set -x 2 | export DTM_DEBUG=1 3 | echo "mode: count" > coverage.txt 4 | for store in redis boltdb mysql postgres sqlserver; do 5 | TEST_STORE=$store go test -failfast -covermode count -coverprofile=profile.out -coverpkg=github.com/dtm-labs/dtm/client/dtmcli,github.com/dtm-labs/dtm/client/dtmcli/dtmimp,github.com/dtm-labs/logger,github.com/dtm-labs/dtm/client/dtmgrpc,github.com/dtm-labs/dtm/client/workflow,github.com/dtm-labs/dtm/client/dtmgrpc/dtmgimp,github.com/dtm-labs/dtm/dtmsvr,dtmsvr/config,github.com/dtm-labs/dtm/dtmsvr/storage,github.com/dtm-labs/dtm/dtmsvr/storage/boltdb,github.com/dtm-labs/dtm/dtmsvr/storage/redis,github.com/dtm-labs/dtm/dtmsvr/storage/registry,github.com/dtm-labs/dtm/dtmsvr/storage/sql,github.com/dtm-labs/dtm/dtmutil -gcflags=-l ./... || exit 1 6 | echo "TEST_STORE=$store finished" 7 | if [ -f profile.out ]; then 8 | cat profile.out | grep -v 'mode:' >> coverage.txt 9 | echo > profile.out 10 | fi 11 | done 12 | ## for local unit test, you may use following command 13 | # SKIP_MONGO=1 TEST_STORE=redis GOARCH=amd64 go test -v -failfast -count=1 -gcflags=all=-l ./... 14 | 15 | # go tool cover -html=coverage.txt 16 | 17 | # curl -s https://codecov.io/bash | bash 18 | -------------------------------------------------------------------------------- /dtm/dtm/qs/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package main 8 | 9 | import ( 10 | "github.com/dtm-labs/dtm/test/busi" 11 | ) 12 | 13 | func main() { 14 | busi.QsMain() 15 | } 16 | -------------------------------------------------------------------------------- /dtm/dtm/revive.toml: -------------------------------------------------------------------------------- 1 | ignoreGeneratedHeader = false 2 | severity = "warning" 3 | confidence = 0.8 4 | errorCode = 0 5 | warningCode = 0 6 | 7 | [rule.blank-imports] 8 | [rule.context-as-argument] 9 | [rule.context-keys-type] 10 | [rule.dot-imports] 11 | [rule.error-return] 12 | [rule.error-strings] 13 | [rule.error-naming] 14 | [rule.exported] 15 | [rule.if-return] 16 | [rule.increment-decrement] 17 | [rule.var-naming] 18 | [rule.var-declaration] 19 | [rule.range] 20 | [rule.receiver-naming] 21 | [rule.time-naming] 22 | [rule.unexported-return] 23 | [rule.indent-error-flow] 24 | [rule.errorf] 25 | [rule.superfluous-else] 26 | [rule.unreachable-code] 27 | [rule.redefines-builtin-id] -------------------------------------------------------------------------------- /dtm/dtm/sqls/busi.mongo.js: -------------------------------------------------------------------------------- 1 | use dtm_busi 2 | db.user_account.drop() 3 | db.user_account.createIndex({ user_id: NumberLong(1) }, { unique: true }) 4 | 5 | db.user_account.insert({ user_id: NumberLong(1), balance: 10000 }) 6 | db.user_account.insert({ user_id: NumberLong(2), balance: 10000 }) 7 | -------------------------------------------------------------------------------- /dtm/dtm/sqls/busi.mysql.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE if not exists dtm_busi 2 | /*!40100 DEFAULT CHARACTER SET utf8mb4 */ 3 | ; 4 | drop table if exists dtm_busi.user_account; 5 | create table if not exists dtm_busi.user_account( 6 | id int(11) PRIMARY KEY AUTO_INCREMENT, 7 | user_id int(11) UNIQUE, 8 | balance DECIMAL(10, 2) not null default '0', 9 | trading_balance DECIMAL(10, 2) not null default '0', 10 | create_time datetime DEFAULT now(), 11 | update_time datetime DEFAULT now(), 12 | key(create_time), 13 | key(update_time) 14 | ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; 15 | insert into dtm_busi.user_account (user_id, balance) 16 | values (1, 10000), 17 | (2, 10000) on DUPLICATE KEY 18 | UPDATE balance = 19 | values (balance); -------------------------------------------------------------------------------- /dtm/dtm/sqls/busi.postgres.sql: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA if not exists dtm_busi 2 | /* SQLINES DEMO *** RACTER SET utf8mb4 */ 3 | ; 4 | drop table if exists dtm_busi.user_account; 5 | -- SQLINES LICENSE FOR EVALUATION USE ONLY 6 | create sequence if not exists dtm_busi.user_account_seq; 7 | create table if not exists dtm_busi.user_account( 8 | id int PRIMARY KEY DEFAULT NEXTVAL ('dtm_busi.user_account_seq'), 9 | user_id int UNIQUE, 10 | balance DECIMAL(10, 2) not null default '0', 11 | trading_balance DECIMAL(10, 2) not null default '0', 12 | create_time timestamp(0) with time zone DEFAULT now(), 13 | update_time timestamp(0) with time zone DEFAULT now() 14 | ); 15 | -- SQLINES LICENSE FOR EVALUATION USE ONLY 16 | create index if not exists create_idx on dtm_busi.user_account(create_time); 17 | -- SQLINES LICENSE FOR EVALUATION USE ONLY 18 | create index if not exists update_idx on dtm_busi.user_account(update_time); 19 | TRUNCATE dtm_busi.user_account; 20 | insert into dtm_busi.user_account (user_id, balance) 21 | values (1, 10000), 22 | (2, 10000); -------------------------------------------------------------------------------- /dtm/dtm/sqls/dtmcli.barrier.mongo.js: -------------------------------------------------------------------------------- 1 | use dtm_barrier 2 | db.barrier.drop() 3 | db.barrier.createIndex({ gid: 1, branch_id: 1, op: 1, barrier_id: 1 }, { unique: true }) 4 | db.barrier.insert({ gid: "123", branch_id: "01", op: "action", barrier_id: "01", reason: "test" }); 5 | -------------------------------------------------------------------------------- /dtm/dtm/sqls/dtmcli.barrier.mysql.sql: -------------------------------------------------------------------------------- 1 | create database if not exists dtm_barrier 2 | /*!40100 DEFAULT CHARACTER SET utf8mb4 */ 3 | ; 4 | drop table if exists dtm_barrier.barrier; 5 | create table if not exists dtm_barrier.barrier( 6 | id bigint(22) PRIMARY KEY AUTO_INCREMENT, 7 | trans_type varchar(45) default '', 8 | gid varchar(128) default '', 9 | branch_id varchar(128) default '', 10 | op varchar(45) default '', 11 | barrier_id varchar(45) default '', 12 | reason varchar(45) default '' comment 'the branch type who insert this record', 13 | create_time datetime DEFAULT now(), 14 | update_time datetime DEFAULT now(), 15 | key(create_time), 16 | key(update_time), 17 | UNIQUE key(gid, branch_id, op, barrier_id) 18 | ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; -------------------------------------------------------------------------------- /dtm/dtm/sqls/dtmcli.barrier.postgres.sql: -------------------------------------------------------------------------------- 1 | create schema if not exists dtm_barrier; 2 | drop table if exists dtm_barrier.barrier; 3 | CREATE SEQUENCE if not EXISTS dtm_barrier.barrier_seq; 4 | create table if not exists dtm_barrier.barrier( 5 | id bigint NOT NULL DEFAULT NEXTVAL ('dtm_barrier.barrier_seq'), 6 | trans_type varchar(45) default '', 7 | gid varchar(128) default '', 8 | branch_id varchar(128) default '', 9 | op varchar(45) default '', 10 | barrier_id varchar(45) default '', 11 | reason varchar(45) default '', 12 | create_time timestamp(0) with time zone DEFAULT NULL, 13 | update_time timestamp(0) with time zone DEFAULT NULL, 14 | PRIMARY KEY(id), 15 | CONSTRAINT uniq_barrier unique(gid, branch_id, op, barrier_id) 16 | ); -------------------------------------------------------------------------------- /dtm/dtm/test/busi/base_jrpc.go: -------------------------------------------------------------------------------- 1 | package busi 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dtm-labs/dtm/client/dtmcli/dtmimp" 7 | "github.com/dtm-labs/dtm/dtmutil" 8 | "github.com/dtm-labs/logger" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | // BusiJrpcURL url prefix for busi 13 | var BusiJrpcURL = fmt.Sprintf("http://localhost:%d/api/json-rpc?method=", BusiPort) 14 | 15 | func addJrpcRoute(app *gin.Engine) { 16 | app.POST("/api/json-rpc", dtmutil.WrapHandler(func(c *gin.Context) interface{} { 17 | var data map[string]interface{} 18 | err := c.BindJSON(&data) 19 | dtmimp.E2P(err) 20 | logger.Debugf("method is: %s", data["method"]) 21 | var rerr map[string]interface{} 22 | r := MainSwitch.JrpcResult.Fetch() 23 | if r != "" { 24 | rerr = map[string]interface{}{ 25 | "code": map[string]int{ 26 | "FAILURE": dtmimp.JrpcCodeFailure, 27 | "ONGOING": dtmimp.JrpcCodeOngoing, 28 | "OTHER": -23977, 29 | }, 30 | } 31 | } 32 | return map[string]interface{}{ 33 | "jsonrpc": "2.0", 34 | "error": rerr, 35 | "id": data["id"], 36 | } 37 | })) 38 | } 39 | -------------------------------------------------------------------------------- /dtm/dtm/test/busi/startup.go: -------------------------------------------------------------------------------- 1 | package busi 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | grpc "google.golang.org/grpc" 6 | ) 7 | 8 | // Startup startup the busi's grpc and http service 9 | func Startup() (*gin.Engine, *grpc.Server) { 10 | svr := GrpcStartup() 11 | app := BaseAppStartup() 12 | return app, svr 13 | } 14 | -------------------------------------------------------------------------------- /dtm/dtm/test/tcc_jrpc_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/dtm-labs/dtm/client/dtmcli" 7 | "github.com/dtm-labs/dtm/client/dtmcli/dtmimp" 8 | "github.com/dtm-labs/dtm/dtmutil" 9 | "github.com/dtm-labs/dtm/test/busi" 10 | "github.com/go-resty/resty/v2" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestTccJrpcNormal(t *testing.T) { 15 | req := busi.GenReqHTTP(30, false, false) 16 | gid := dtmimp.GetFuncName() 17 | err := dtmcli.TccGlobalTransaction2(dtmutil.DefaultJrpcServer, gid, func(tcc *dtmcli.Tcc) { 18 | tcc.Protocol = dtmimp.Jrpc 19 | }, func(tcc *dtmcli.Tcc) (*resty.Response, error) { 20 | _, err := tcc.CallBranch(req, Busi+"/TransOut", Busi+"/TransOutConfirm", Busi+"/TransOutRevert") 21 | assert.Nil(t, err) 22 | return tcc.CallBranch(req, Busi+"/TransIn", Busi+"/TransInConfirm", Busi+"/TransInRevert") 23 | }) 24 | assert.Nil(t, err) 25 | waitTransProcessed(gid) 26 | assert.Equal(t, StatusSucceed, getTransStatus(gid)) 27 | assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(gid)) 28 | } 29 | -------------------------------------------------------------------------------- /dtm/dtm/test/workflow_base_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 yedf. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | package test 8 | 9 | import ( 10 | "testing" 11 | "time" 12 | 13 | "github.com/dtm-labs/dtm/client/dtmcli" 14 | "github.com/dtm-labs/dtm/client/dtmcli/dtmimp" 15 | "github.com/dtm-labs/dtm/dtmsvr" 16 | "github.com/dtm-labs/dtm/dtmsvr/storage" 17 | "github.com/stretchr/testify/assert" 18 | ) 19 | 20 | func TestWorkflowBranchConflict(t *testing.T) { 21 | gid := dtmimp.GetFuncName() 22 | store := dtmsvr.GetStore() 23 | now := time.Now() 24 | g := &storage.TransGlobalStore{ 25 | Gid: gid, 26 | Status: dtmcli.StatusPrepared, 27 | NextCronTime: &now, 28 | } 29 | err := store.MaySaveNewTrans(g, []storage.TransBranchStore{ 30 | { 31 | BranchID: "00", 32 | Op: dtmimp.OpAction, 33 | }, 34 | }) 35 | assert.Nil(t, err) 36 | err = dtmimp.CatchP(func() { 37 | store.LockGlobalSaveBranches(gid, dtmcli.StatusPrepared, []storage.TransBranchStore{ 38 | {BranchID: "00", Op: dtmimp.OpAction}, 39 | }, -1) 40 | }) 41 | assert.Error(t, err) 42 | store.ChangeGlobalStatus(g, StatusSucceed, []string{}, true) 43 | } 44 | -------------------------------------------------------------------------------- /dtm/dtm/test/workflow_http_ret_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/dtm-labs/dtm/client/dtmcli/dtmimp" 7 | "github.com/dtm-labs/dtm/client/workflow" 8 | "github.com/dtm-labs/dtm/test/busi" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestWorkflowRet(t *testing.T) { 13 | workflow.SetProtocolForTest(dtmimp.ProtocolHTTP) 14 | req := busi.GenReqHTTP(30, false, false) 15 | gid := dtmimp.GetFuncName() 16 | 17 | workflow.Register2(gid, func(wf *workflow.Workflow, data []byte) ([]byte, error) { 18 | var req busi.ReqHTTP 19 | dtmimp.MustUnmarshal(data, &req) 20 | _, err := wf.NewBranch().NewRequest().SetBody(req).Post(Busi + "/TransOut") 21 | return []byte("result of workflow"), err 22 | }) 23 | 24 | ret, err := workflow.Execute2(gid, gid, dtmimp.MustMarshal(req)) 25 | assert.Nil(t, err) 26 | assert.Equal(t, "result of workflow", string(ret)) 27 | assert.Equal(t, StatusSucceed, getTransStatus(gid)) 28 | 29 | // the second execute will return result directly 30 | ret, err = workflow.Execute2(gid, gid, dtmimp.MustMarshal(req)) 31 | assert.Nil(t, err) 32 | assert.Equal(t, "result of workflow", string(ret)) 33 | assert.Equal(t, StatusSucceed, getTransStatus(gid)) 34 | } 35 | -------------------------------------------------------------------------------- /dtm/dtm/test/workflow_interceptor_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/dtm-labs/dtm/client/workflow" 8 | "github.com/stretchr/testify/assert" 9 | "google.golang.org/grpc" 10 | ) 11 | 12 | func TestWorkflowInterceptorOutsideSaga(t *testing.T) { 13 | called := false 14 | workflow.Interceptor(context.TODO(), "method", nil, nil, &grpc.ClientConn{}, func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error { 15 | called = true 16 | return nil 17 | }) 18 | assert.True(t, called) 19 | } 20 | -------------------------------------------------------------------------------- /dtm/quick-start-sample/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | quick-start-sample 15 | # Dependency directories (remove the comment below to include it) 16 | # vendor/ 17 | -------------------------------------------------------------------------------- /dtm/quick-start-sample/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 DTM Development and Communities 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dtm/quick-start-sample/README-cn.md: -------------------------------------------------------------------------------- 1 | 简体中文 | [English](./README.md) 2 | 3 | # quick-start-sample 4 | dtm go 客户端[client](https://github.com/dtm-labs/client) 的最简单例子. 5 | 6 | 这个仓库包括了4个项目: 7 | - [dtmcli-qs](./dtmcli-qs/README-cn.md): 演示http客户端dtmcli的接入 8 | - [dtmgrpc-qs](./dtmgrpc-qs/README-cn.md): 演示grpc客户端dtmgrpc的接入 9 | - [workflow-http](./workflow-http/README-cn.md): 演示workflow的http接入 10 | - [workflow-grpc](./workflow-grpc/README-cn.md): 演示workflow的grpc接入 11 | -------------------------------------------------------------------------------- /dtm/quick-start-sample/README.md: -------------------------------------------------------------------------------- 1 | English | [简体中文](./README-cn.md) 2 | 3 | # quick-start-sample 4 | quick start sample for [client](https://github.com/dtm-labs/client) of dtm. 5 | 6 | This repo includes 4 project: 7 | - [dtmcli-qs](./dtmcli-qs/README.md): illustrate the usage of http client 8 | - [dtmgrpc-qs](./dtmgrpc-qs/README.md): illustrate the usage of grpc client 9 | - [workflow-http](./workflow-http/README.md): illustrate the usage of workflow using http 10 | - [workflow-grpc](./workflow-grpc/README.md): illustrate the usage of workflow using grpc 11 | -------------------------------------------------------------------------------- /dtm/quick-start-sample/busi/busi.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package busi; 4 | 5 | option go_package = "./busi"; 6 | 7 | // The dtm service definition. 8 | service Busi { 9 | rpc TransInRevert(BusiReq) returns (BusiReply) {} 10 | rpc TransOutRevert(BusiReq) returns (BusiReply) {} 11 | 12 | rpc TransIn(BusiReq) returns (BusiReply) {} 13 | rpc TransOut(BusiReq) returns (BusiReply) {} 14 | } 15 | 16 | message BusiReq { 17 | int64 Amount = 1; 18 | int64 UserID = 2; 19 | string TransOutResult = 3; 20 | string TransInResult = 4; 21 | } 22 | 23 | message BusiReply { 24 | string Message = 1; 25 | } -------------------------------------------------------------------------------- /dtm/quick-start-sample/dtmcli-qs/README-cn.md: -------------------------------------------------------------------------------- 1 | # dtmcli-qs 2 | client/dtmcli的最简go使用示例 3 | 4 | ## 快速开始 5 | 6 | ### 安装运行dtm 7 | 8 | 参考[dtm安装运行](https://dtm.pub/guide/install.html) 9 | 10 | ### 启动示例 11 | 12 | ``` bash 13 | go run main.go 14 | ``` 15 | 16 | ### 输出 17 | 18 | 可以从dtmcli-qs的日志里看到执行的顺序如下: 19 | 20 | - TransOut 21 | - TransIn 22 | 23 | 整个saga事务执行成功 24 | 25 | ### 示例解读 26 | 27 | ``` GO 28 | // 具体业务微服务地址 29 | const qsBusi = "http://localhost:8081/api/busi_saga" 30 | req := &gin.H{"amount": 30} // 微服务的载荷 31 | // DtmServer为DTM服务的地址,是一个url 32 | DtmServer := "http://localhost:36789/api/dtmsvr" 33 | saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)). 34 | // 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 补偿操作为url: qsBusi+"/TransOutCom" 35 | Add(qsBusi+"/TransOut", qsBusi+"/TransOutCom", req). 36 | // 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransIn", 补偿操作为url: qsBusi+"/TransInCom" 37 | Add(qsBusi+"/TransIn", qsBusi+"/TransInCom", req) 38 | // 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务 39 | err := saga.Submit() 40 | ``` 41 | 42 | ### 更多示例,详见[dtm-examples](https://github.com/dtm-labs/dtm-examples) 43 | -------------------------------------------------------------------------------- /dtm/quick-start-sample/dtmgrpc-qs/README-cn.md: -------------------------------------------------------------------------------- 1 | # dtmgrpc-qs 2 | client/dtmgrpc的最简go使用示例 3 | 4 | ## 快速开始 5 | 6 | ### 安装运行dtm 7 | 8 | 参考[dtm安装运行](https://dtm.pub/guide/install.html) 9 | 10 | ### 启动示例 11 | 12 | ``` bash 13 | go run main.go 14 | ``` 15 | 16 | ### 输出 17 | 18 | 可以从dtmcli-qs的日志里看到执行的顺序如下: 19 | 20 | - TransOut 21 | - TransIn 22 | 23 | 整个saga事务执行成功 24 | 25 | ### 示例解读 26 | 27 | ``` GO 28 | gid := shortuuid.New() // 生成gid 29 | req := &busi.BusiReq{Amount: 30} // 微服务的载荷 30 | 31 | saga := dtmgrpc.NewSagaGrpc(busi.DtmGrpcServer, gid). 32 | // 添加一个TransOut的子事务,正向操作为grpc的url: busi.BusiGrpc+"/busi.Busi/TransOut", 补偿操作类似 33 | Add(busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutRevert", req). 34 | // 添加一个TransIn的子事务,正向操作为grpc的url: busi.BusiGrpc+"/busi.Busi/TransIn", 补偿操作类似 35 | Add(busi.BusiGrpc+"/busi.Busi/TransIn", busi.BusiGrpc+"/busi.Busi/TransInRevert", req) 36 | // 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务 37 | err := saga.Submit() 38 | ``` 39 | 40 | ### 更多示例,详见[dtm-examples](https://github.com/dtm-labs/dtm-examples) 41 | -------------------------------------------------------------------------------- /dtm/quick-start-sample/dtmgrpc-qs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/dtm-labs/client/dtmcli/logger" 7 | "github.com/dtm-labs/client/dtmgrpc" 8 | "github.com/dtm-labs/quick-start-sample/busi" 9 | "github.com/lithammer/shortuuid/v3" 10 | ) 11 | 12 | func main() { 13 | s := busi.GrpcNewServer() 14 | busi.GrpcStartup(s) 15 | logger.Infof("grpc simple transaction begin") 16 | gid := shortuuid.New() 17 | req := &busi.BusiReq{Amount: 30} 18 | // req := &busi.BusiReq{Amount: 30, TransInResult: "FAILURE"} 19 | saga := dtmgrpc.NewSagaGrpc(busi.DtmGrpcServer, gid). 20 | Add(busi.BusiGrpc+"/busi.Busi/TransOut", busi.BusiGrpc+"/busi.Busi/TransOutRevert", req). 21 | Add(busi.BusiGrpc+"/busi.Busi/TransIn", busi.BusiGrpc+"/busi.Busi/TransInRevert", req) 22 | err := saga.Submit() 23 | if err != nil { 24 | panic(err) 25 | } 26 | time.Sleep(3 * time.Second) 27 | } 28 | -------------------------------------------------------------------------------- /dtm/quick-start-sample/dummy.go: -------------------------------------------------------------------------------- 1 | package quickstartsample 2 | 3 | import ( 4 | _ "github.com/dtm-labs/client/dtmcli" 5 | _ "github.com/dtm-labs/client/dtmgrpc" 6 | _ "github.com/dtm-labs/client/workflow" 7 | _ "github.com/dtm-labs/quick-start-sample/busi" 8 | ) 9 | -------------------------------------------------------------------------------- /dtm/quick-start-sample/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dtm-labs/quick-start-sample 2 | 3 | go 1.16 4 | 5 | // replace github.com/dtm-labs/client => ../client 6 | 7 | require ( 8 | github.com/dtm-labs/client v1.17.3 9 | github.com/dtm-labs/logger v0.0.2 // indirect 10 | github.com/gin-gonic/gin v1.8.1 11 | github.com/klauspost/compress v1.16.5 // indirect 12 | github.com/lithammer/shortuuid/v3 v3.0.7 13 | github.com/montanaflynn/stats v0.7.1 // indirect 14 | github.com/xdg-go/scram v1.1.2 // indirect 15 | github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect 16 | go.mongodb.org/mongo-driver v1.11.6 // indirect 17 | go.uber.org/atomic v1.11.0 // indirect 18 | go.uber.org/multierr v1.11.0 // indirect 19 | go.uber.org/zap v1.24.0 // indirect 20 | golang.org/x/crypto v0.9.0 // indirect 21 | golang.org/x/sync v0.2.0 // indirect 22 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 23 | google.golang.org/grpc v1.55.0 24 | google.golang.org/protobuf v1.30.0 25 | ) 26 | -------------------------------------------------------------------------------- /dtm/sql_dtm.sql: -------------------------------------------------------------------------------- 1 | create schema dtm_busi; 2 | 3 | 4 | CREATE SEQUENCE IF NOT EXISTS dtm_busi.user_account_id_seq 5 | INCREMENT 1 6 | START 1 7 | MINVALUE 1 8 | MAXVALUE 9223372036854775807 9 | CACHE 1; 10 | CREATE TABLE dtm_busi.user_account ( 11 | id bigint NOT NULL DEFAULT nextval('dtm_busi.user_account_id_seq'::regclass) PRIMARY KEY, 12 | user_id bigint not NULL UNIQUE , 13 | balance decimal(10,2) NOT NULL DEFAULT '0.00', 14 | trading_balance decimal(10,2) NOT NULL DEFAULT '0.00', 15 | create_time timestamp without time zone DEFAULT now(), 16 | update_time timestamp without time zone DEFAULT now() 17 | ); -------------------------------------------------------------------------------- /elsticsearch/README.md: -------------------------------------------------------------------------------- 1 | # elastic search 2 | ## account: 3 | ``` 4 | elastic 5 | ZQjqfRh4X6gQZPgTgh4iv6n6 6 | ``` -------------------------------------------------------------------------------- /elsticsearch/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "open-dev/elsticsearch/query" 7 | ) 8 | 9 | func main() { 10 | // query.AddDoc() 11 | query.Search() 12 | fmt.Println("add success") 13 | } 14 | -------------------------------------------------------------------------------- /gin-web-framework/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/gin-web-framework/README.md -------------------------------------------------------------------------------- /gin-web-framework/apiExample/index.go: -------------------------------------------------------------------------------- 1 | package apiexample 2 | -------------------------------------------------------------------------------- /gin-web-framework/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | -------------------------------------------------------------------------------- /http/keep-alive/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func Index(w http.ResponseWriter, r *http.Request) { 9 | fmt.Println("receive a request from:", r.RemoteAddr, r.Header) 10 | w.Write([]byte("ok")) 11 | } 12 | 13 | func main() { 14 | fmt.Println("server keep alive") 15 | var s = http.Server{ 16 | Addr: ":8080", 17 | Handler: http.HandlerFunc(Index), 18 | } 19 | s.SetKeepAlivesEnabled(false) 20 | s.ListenAndServe() 21 | } 22 | -------------------------------------------------------------------------------- /http/request/http.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "net/http" 7 | "net/url" 8 | ) 9 | 10 | func HttpRequest( 11 | urlStr string, 12 | method string, 13 | headers map[string]string, 14 | params map[string]string, 15 | data any) (*http.Response, error) { 16 | // 创建URL 17 | u, err := url.Parse(urlStr) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | // 添加查询参数 23 | query := u.Query() 24 | for k, v := range params { 25 | query.Set(k, v) 26 | } 27 | u.RawQuery = query.Encode() 28 | 29 | // 将数据编码为JSON 30 | buf := new(bytes.Buffer) 31 | if data != nil { 32 | b, err := json.Marshal(data) 33 | if err != nil { 34 | return nil, err 35 | } 36 | buf = bytes.NewBuffer(b) 37 | } 38 | 39 | // 创建请求 40 | req, err := http.NewRequest(method, u.String(), buf) 41 | 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | for k, v := range headers { 47 | req.Header.Set(k, v) 48 | } 49 | 50 | if data != nil { 51 | req.Header.Set("Content-Type", "application/json") 52 | } 53 | 54 | // 发送请求 55 | resp, err := http.DefaultClient.Do(req) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | // 返回响应,让调用者处理 61 | return resp, nil 62 | } 63 | -------------------------------------------------------------------------------- /interview/devops/DevOps__1718418692.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/interview/devops/DevOps__1718418692.pdf -------------------------------------------------------------------------------- /kafka/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/kafka/.DS_Store -------------------------------------------------------------------------------- /kafka/images/at_least_once_delivery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/kafka/images/at_least_once_delivery.png -------------------------------------------------------------------------------- /kafka/images/at_most_once_delivery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/kafka/images/at_most_once_delivery.png -------------------------------------------------------------------------------- /kafka/images/consumer_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/kafka/images/consumer_group.png -------------------------------------------------------------------------------- /kafka/images/segment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/kafka/images/segment.png -------------------------------------------------------------------------------- /kafka/images/segment1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/kafka/images/segment1.png -------------------------------------------------------------------------------- /kafka/images/segment2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/kafka/images/segment2.png -------------------------------------------------------------------------------- /kafka/images/segment3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/kafka/images/segment3.png -------------------------------------------------------------------------------- /kafka/mskaws/README.md: -------------------------------------------------------------------------------- 1 | # demo aws msk 2 | -------------------------------------------------------------------------------- /letgo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/.DS_Store -------------------------------------------------------------------------------- /letgo/README.md: -------------------------------------------------------------------------------- 1 | # Lets Go 2 | 3 | ## Chapter 2.3: Web Application Basics 4 | - link: https://github.com/ducnpdev/open-dev/tree/master/letgo/chapter2.3 5 | ## Chapter 2.4: Routing Requests 6 | - link: https://github.com/ducnpdev/open-dev/tree/master/letgo/chapter2.4 7 | -------------------------------------------------------------------------------- /letgo/chapter2.3/README.md: -------------------------------------------------------------------------------- 1 | # Lets Go 2 | 3 | ## Chapter 2.3: Web Application Basics 4 | - Đầu tiên cần 1 func handler, để xử lý logic. Giống như MVC, handler dùng để xử lý logic, nhận request từ client và response data thông qua HTTP. 5 | 6 | - Cần có 1 router, trong này mình dùng http.NewServeMux, sàu này có thể dùng Gin, Echo. 7 | 8 | - Sau cùng là 1 máy chủ, mục đích để dợi nhận request từ client, sau này có thể dùng Nginx hoặc Apache. 9 | ```go 10 | package main 11 | import ( 12 | "log" 13 | "net/http" 14 | ) 15 | func home(w http.ResponseWriter, r *http.Request) { 16 | w.Write([]byte("Hello from OpenDev")) 17 | } 18 | func main() { 19 | mux := http.NewServeMux() 20 | mux.HandleFunc("/", home) 21 | log.Println("Starting server on :4000") 22 | err := http.ListenAndServe(":4000", mux) 23 | log.Fatal(err) 24 | } 25 | ``` 26 | 27 | - Chú ý: khi start `go run main.go` web sẽ lắng nghe trên port 4000, mở web tại đường dẫn: http://localhost:4000 -------------------------------------------------------------------------------- /letgo/chapter2.3/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.3/code.png -------------------------------------------------------------------------------- /letgo/chapter2.3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | // Define a home handler function which writes a byte slice containing 9 | // "Hello from Snippetbox" as the response body. 10 | func home(w http.ResponseWriter, r *http.Request) { 11 | w.Write([]byte("Hello from OpenDev")) 12 | } 13 | func main() { 14 | // Use the http.NewServeMux() function to initialize a new servemux, then 15 | // register the home function as the handler for the "/" URL pattern. 16 | mux := http.NewServeMux() 17 | mux.HandleFunc("/", home) 18 | // Use the http.ListenAndServe() function to start a new web server. We pas 19 | // two parameters: the TCP network address to listen on (in this case ":400 20 | // and the servemux we just created. If http.ListenAndServe() returns an er 21 | // we use the log.Fatal() function to log the error message and exit. 22 | log.Println("Starting server on :4000") 23 | err := http.ListenAndServe(":4000", mux) 24 | log.Fatal(err) 25 | } 26 | -------------------------------------------------------------------------------- /letgo/chapter2.3/readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.3/readme.png -------------------------------------------------------------------------------- /letgo/chapter2.3/web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.3/web.png -------------------------------------------------------------------------------- /letgo/chapter2.4/create_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.4/create_user.png -------------------------------------------------------------------------------- /letgo/chapter2.4/list_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.4/list_user.png -------------------------------------------------------------------------------- /letgo/chapter2.4/page_not_found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.4/page_not_found.png -------------------------------------------------------------------------------- /letgo/chapter2.9/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.9/code.png -------------------------------------------------------------------------------- /letgo/chapter2.9/css_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.9/css_code.png -------------------------------------------------------------------------------- /letgo/chapter2.9/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.9/folder.png -------------------------------------------------------------------------------- /letgo/chapter2.9/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | func fileStatis(w http.ResponseWriter, r *http.Request) { 9 | w.Write([]byte("danh sach user OpenDev...")) 10 | } 11 | 12 | func main() { 13 | // Use the http.NewServeMux() function to initialize a new servemux, then 14 | // register the home function as the handler for the "/" URL pattern. 15 | mux := http.NewServeMux() 16 | 17 | fileServer := http.FileServer(http.Dir("./ui/static/")) 18 | // Use the mux.Handle() function to register the file server as the handler 19 | // all URL paths that start with "/static/". For matching paths, we strip t 20 | // "/static" prefix before the request reaches the file server. 21 | mux.Handle("/static/", http.StripPrefix("/static", fileServer)) 22 | 23 | log.Println("Starting server on :4000") 24 | err := http.ListenAndServe(":4000", mux) 25 | log.Fatal(err) 26 | } 27 | -------------------------------------------------------------------------------- /letgo/chapter2.9/static_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.9/static_file.png -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/efs.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "embed" 5 | ) 6 | 7 | //go:embed "html" "static" 8 | var Files embed.FS 9 | -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/html/base.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{define "base"}} 4 | 5 | 6 | 7 | 8 | {{template "title" .}} - Snippetbox 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Snippetbox

16 |
17 | {{template "nav" .}} 18 |
19 | {{with .Flash}} 20 |
{{.}}
21 | {{end}} 22 | {{template "main" .}} 23 |
24 |
25 | Powered by Go in {{.CurrentYear}} 26 |
27 | 28 | 29 | 30 | {{end}} -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/html/pages/home.tmpl: -------------------------------------------------------------------------------- 1 | {{define "title"}}Home{{end}} 2 | 3 | {{define "main"}} 4 |

Latest Snippets

5 | {{if .Snippets}} 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{range .Snippets}} 13 | 14 | 15 | 16 | 17 | 18 | {{end}} 19 |
TitleCreatedID
{{.Title}}{{humanDate .Created}}#{{.ID}}
20 | {{else}} 21 |

There's nothing to see here... yet!

22 | {{end}} 23 | {{end}} -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/html/pages/login.tmpl: -------------------------------------------------------------------------------- 1 | {{define "title"}}Login{{end}} 2 | 3 | {{define "main"}} 4 |
5 | 6 | {{range .Form.NonFieldErrors}} 7 |
{{.}}
8 | {{end}} 9 |
10 | 11 | {{with .Form.FieldErrors.email}} 12 | 13 | {{end}} 14 | 15 |
16 |
17 | 18 | {{with .Form.FieldErrors.password}} 19 | 20 | {{end}} 21 | 22 |
23 |
24 | 25 |
26 |
27 | {{end}} -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/html/pages/signup.tmpl: -------------------------------------------------------------------------------- 1 | {{define "title"}}Signup{{end}} 2 | 3 | {{define "main"}} 4 |
5 | 6 |
7 | 8 | {{with .Form.FieldErrors.name}} 9 | 10 | {{end}} 11 | 12 |
13 |
14 | 15 | {{with .Form.FieldErrors.email}} 16 | 17 | {{end}} 18 | 19 |
20 |
21 | 22 | {{with .Form.FieldErrors.password}} 23 | 24 | {{end}} 25 | 26 |
27 |
28 | 29 |
30 |
31 | {{end}} -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/html/pages/view.tmpl: -------------------------------------------------------------------------------- 1 | {{define "title"}}Snippet #{{.Snippet.ID}}{{end}} 2 | 3 | {{define "main"}} 4 | {{with .Snippet}} 5 |
6 | 10 |
{{.Content}}
11 | 15 |
16 | {{end}} 17 | {{end}} -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/html/partials/nav.tmpl: -------------------------------------------------------------------------------- 1 | {{define "nav"}} 2 | 21 | {{end}} -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.9/ui/static/img/favicon.ico -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/letgo/chapter2.9/ui/static/img/logo.png -------------------------------------------------------------------------------- /letgo/chapter2.9/ui/static/js/main.js: -------------------------------------------------------------------------------- 1 | var navLinks = document.querySelectorAll("nav a"); 2 | for (var i = 0; i < navLinks.length; i++) { 3 | var link = navLinks[i] 4 | if (link.getAttribute('href') == window.location.pathname) { 5 | link.classList.add("live"); 6 | break; 7 | } 8 | } -------------------------------------------------------------------------------- /performances/README.MD: -------------------------------------------------------------------------------- 1 | # performance 2 | ## Standard 3 | - Test simple of function return error: https://opendev.hashnode.dev/golang-test-performance-function-standard-1 4 | - comparing 2 function: 5 | ```go 6 | func newErr() error { 7 | return errors.new("this is error") 8 | } 9 | func fmtErr() error { 10 | return fmt.Errorf("this is error") 11 | } 12 | ``` 13 | - Test convert string to int of 3 function: https://opendev.hashnode.dev/golang-test-performance-function-standard-1 14 | - comparing 3 function: 15 | - comparing 2 function: 16 | ```go 17 | func MethodInt(i int) string { 18 | return strconv.FormatInt(int64(i), 10) 19 | } 20 | func MethodItoa(i int) string { 21 | return strconv.Itoa(i) 22 | } 23 | func MethodFmt(i int) string { 24 | return fmt.Sprintf("%d", i) 25 | } 26 | ``` -------------------------------------------------------------------------------- /performances/standard/condition_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func CheckIfElseEmpty(s string) string { 9 | if s == "sdfsdsdfsdsdfsdsdfsdsdfsdsdfsd" { 10 | return s 11 | } 12 | return s 13 | } 14 | func CheckIfElseLen(s string) string { 15 | if len(s) == 30 { 16 | return s 17 | } 18 | return s 19 | } 20 | 21 | func CheckIfElseEqual(s string) string { 22 | if strings.EqualFold(s, "sdfsdsdfsdsdfsdsdfsdsdfsdsdfsd") { 23 | return s 24 | } 25 | return s 26 | } 27 | 28 | func BenchmarkIfElseEmpty(b *testing.B) { 29 | for i := 0; i < b.N; i++ { 30 | // TODO: Your Code Here 31 | CheckIfElseEmpty("sdfsdsdfsdsdfsdsdfsdsdfsdsdfsd") 32 | } 33 | } 34 | 35 | func BenchmarkIfElseEqual(b *testing.B) { 36 | for i := 0; i < b.N; i++ { 37 | // TODO: Your Code Here 38 | CheckIfElseEqual("sdfsdsdfsdsdfsdsdfsdsdfsdsdfsd") 39 | } 40 | } 41 | func BenchmarkIfElseLen(b *testing.B) { 42 | for i := 0; i < b.N; i++ { 43 | // TODO: Your Code Here 44 | CheckIfElseLen("anc") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /performances/standard/convert.go: -------------------------------------------------------------------------------- 1 | package main 2 | -------------------------------------------------------------------------------- /performances/standard/convert_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | func MethodInt(i int) string { 10 | return strconv.FormatInt(int64(i), 10) 11 | } 12 | func MethodItoa(i int) string { 13 | return strconv.Itoa(i) 14 | } 15 | func MethodFmt(i int) string { 16 | return fmt.Sprintf("%d", i) 17 | } 18 | func BenchmarkFmt(b *testing.B) { 19 | // TODO: Initialize 20 | number := 10 21 | for i := 0; i < b.N; i++ { 22 | // TODO: Your Code Here 23 | MethodFmt(number) 24 | } 25 | } 26 | 27 | func BenchmarkMethodInt(b *testing.B) { 28 | // TODO: Initialize 29 | number := 10 30 | for i := 0; i < b.N; i++ { 31 | // TODO: Your Code Here 32 | MethodInt(number) 33 | } 34 | } 35 | func BenchmarkMethodItoa(b *testing.B) { 36 | // TODO: Initialize 37 | number := 10 38 | for i := 0; i < b.N; i++ { 39 | // TODO: Your Code Here 40 | MethodItoa(number) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /redis/cluster/7000/redis.conf: -------------------------------------------------------------------------------- 1 | port 7000 2 | cluster-enabled yes 3 | cluster-config-file nodes.conf 4 | cluster-node-timeout 5000 5 | appendonly yes 6 | bind 0.0.0.0 -------------------------------------------------------------------------------- /redis/cluster/7001/redis.conf: -------------------------------------------------------------------------------- 1 | port 7000 2 | cluster-enabled yes 3 | cluster-config-file nodes.conf 4 | cluster-node-timeout 5000 5 | appendonly yes 6 | bind 0.0.0.0 -------------------------------------------------------------------------------- /redis/cluster/7002/redis.conf: -------------------------------------------------------------------------------- 1 | port 7000 2 | cluster-enabled yes 3 | cluster-config-file nodes.conf 4 | cluster-node-timeout 5000 5 | appendonly yes 6 | bind 0.0.0.0 -------------------------------------------------------------------------------- /redis/cluster/7003/redis.conf: -------------------------------------------------------------------------------- 1 | port 7000 2 | cluster-enabled yes 3 | cluster-config-file nodes.conf 4 | cluster-node-timeout 5000 5 | appendonly yes 6 | bind 0.0.0.0 -------------------------------------------------------------------------------- /redis/cluster/7004/redis.conf: -------------------------------------------------------------------------------- 1 | port 7000 2 | cluster-enabled yes 3 | cluster-config-file nodes.conf 4 | cluster-node-timeout 5000 5 | appendonly yes 6 | bind 0.0.0.0 -------------------------------------------------------------------------------- /redis/cluster/7005/redis.conf: -------------------------------------------------------------------------------- 1 | port 7000 2 | cluster-enabled yes 3 | cluster-config-file nodes.conf 4 | cluster-node-timeout 5000 5 | appendonly yes 6 | bind 0.0.0.0 -------------------------------------------------------------------------------- /redis/cluster/README.md: -------------------------------------------------------------------------------- 1 | # redis cluster 2 | 3 | docker run -d -it -v `pwd`/7000:/redis --name node-1 -p 6380:7000 --network redis-cluster redis redis-server /redis/redis.conf 4 | docker run -d -it -v `pwd`/7001:/redis --name node-2 -p 6381:7000 --network redis-cluster redis redis-server /redis/redis.conf 5 | docker run -d -it -v `pwd`/7002:/redis --name node-3 -p 6382:7000 --network redis-cluster redis redis-server /redis/redis.conf -------------------------------------------------------------------------------- /redis/custom-values.yaml: -------------------------------------------------------------------------------- 1 | master: 2 | password: "integration" 3 | resources: 4 | requests: 5 | memory: "1Gi" 6 | cpu: "500m" # 0.5 CPU 7 | limits: 8 | cpu: "1000m" 9 | memory: "2Gi" 10 | persistence: 11 | enabled: true 12 | 13 | pdb: 14 | create: true 15 | minAvailable: 1 16 | maxUnavailable: "" 17 | 18 | architecture: standalone 19 | 20 | metrics: 21 | enabled: true 22 | service: 23 | type: LoadBalancer -------------------------------------------------------------------------------- /redis/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "open-dev/redis/redisPkg" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | ctx := context.Background() 12 | fmt.Println("") 13 | redis := redisPkg.InitRedis() 14 | fmt.Println(redis) 15 | err := redis.Set(ctx, "demo-key-123", "", time.Duration(time.Second)*2).Err() 16 | fmt.Println(err) 17 | } 18 | -------------------------------------------------------------------------------- /redis/network.yaml: -------------------------------------------------------------------------------- 1 | # allow all traffic 2 | apiVersion: networking.k8s.io/v1 3 | kind: NetworkPolicy 4 | metadata: 5 | name: allow-all-egress 6 | spec: 7 | podSelector: {} 8 | egress: 9 | - {} 10 | ingress: 11 | - {} 12 | policyTypes: 13 | - Ingress 14 | - Egress 15 | 16 | # Allow all egress traffic 17 | # apiVersion: networking.k8s.io/v1 18 | # kind: NetworkPolicy 19 | # metadata: 20 | # name: allow-all-egress 21 | # spec: 22 | # podSelector: {} 23 | # egress: 24 | # - {} 25 | # policyTypes: 26 | # - Egress 27 | 28 | # Allow all ingress traffic 29 | # apiVersion: networking.k8s.io/v1 30 | # kind: NetworkPolicy 31 | # metadata: 32 | # name: allow-all-ingress 33 | # spec: 34 | # podSelector: {} 35 | # ingress: 36 | # - {} 37 | # policyTypes: 38 | # - Ingress 39 | 40 | # --- 41 | # apiVersion: networking.k8s.io/v1 42 | # kind: NetworkPolicy 43 | # metadata: 44 | # name: default-deny-egress 45 | # spec: 46 | # podSelector: {} 47 | # policyTypes: 48 | # - Egress -------------------------------------------------------------------------------- /redis/pipeline/README.md: -------------------------------------------------------------------------------- 1 | # pipeline 2 | 3 | ## redis ZRANGE: 4 | ### usecase: 5 | - requirement: handle logic attempt access for period 6 | ### implement 7 | - document: https://redis.io/commands/zadd/ 8 | - code example: https://github.com/ducnpdev/open-dev/blob/master/redis/pipeline/range.go 9 | 10 | ### benchmark 11 | - execute only one 12 | ``` 13 | go test -bench=. 14 | ``` 15 | - execute with count 16 | ``` 17 | go test -bench=. -count 5 18 | ``` 19 | - output: 20 | ``` 21 | goos: darwin 22 | goarch: arm64 23 | pkg: open-dev/redis/pipeline 24 | BenchmarkPrimeNumbers-8 1303 848796 ns/op 25 | BenchmarkPrimeNumbers-8 1336 850908 ns/op 26 | BenchmarkPrimeNumbers-8 1425 812590 ns/op 27 | PASS 28 | ok open-dev/redis/pipeline 5.469s 29 | ``` -------------------------------------------------------------------------------- /redis/pipeline/range_test.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkPrimeNumbers(b *testing.B) { 9 | ctx := context.Background() 10 | pline := pipeline() 11 | for i := 0; i < b.N; i++ { 12 | checkattempt(ctx, pline) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /redis/pubsub/pub/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | 8 | "github.com/go-redis/redis/v8" 9 | "github.com/google/uuid" 10 | ) 11 | 12 | func main() { 13 | client := redis.NewClient(&redis.Options{ 14 | Addr: "localhost:6379", // Địa chỉ Redis server 15 | }) 16 | err := client.Ping(context.TODO()).Err() 17 | if err != nil { 18 | panic(err) 19 | } 20 | for i := 0; i < 1000; i++ { 21 | msg := uuid.New().String() 22 | err = client.Publish(context.TODO(), "opendev-channel", msg).Err() 23 | if err != nil { 24 | panic(err) 25 | } 26 | log.Println("Message published!", msg) 27 | time.Sleep(time.Second) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /redis/pubsub/sub/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/go-redis/redis/v8" 8 | ) 9 | 10 | func main() { 11 | client := redis.NewClient(&redis.Options{ 12 | Addr: "localhost:6379", 13 | }) 14 | err := client.Ping(context.TODO()).Err() 15 | if err != nil { 16 | panic(err) 17 | } 18 | // Đăng ký nhận thông báo từ channel "mychannel" 19 | pubsub := client.Subscribe(context.TODO(), "opendev-channel") 20 | 21 | // Nhận thông báo đầu tiên 22 | for { 23 | msg, err := pubsub.ReceiveMessage(context.TODO()) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | log.Printf("Received message from channel: %s", msg.Payload) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /redis/queue/consumer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/go-redis/redis/v8" 9 | ) 10 | 11 | func main() { 12 | 13 | ctx := context.TODO() 14 | 15 | redisClient := redis.NewClient(&redis.Options{ 16 | Addr: "localhost:6379", 17 | Password: "", 18 | DB: 0, 19 | }) 20 | 21 | for { 22 | 23 | result, err := redisClient.BLPop(ctx, 0*time.Second, "payments").Result() 24 | 25 | if err != nil { 26 | fmt.Println(err.Error()) 27 | } else { 28 | for item := range result { 29 | str := result[item] 30 | fmt.Println(str) 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /redis/queue/producer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "net/http" 8 | 9 | "github.com/go-redis/redis/v8" 10 | ) 11 | 12 | func main() { 13 | http.HandleFunc("/payments", paymentsHandler) 14 | http.ListenAndServe(":8080", nil) 15 | } 16 | 17 | func paymentsHandler(w http.ResponseWriter, req *http.Request) { 18 | 19 | redisClient := redis.NewClient(&redis.Options{ 20 | Addr: "localhost:6379", 21 | Password: "", 22 | DB: 0, 23 | }) 24 | 25 | ctx := context.TODO() 26 | 27 | buf := new(bytes.Buffer) 28 | 29 | // Include a Validation logic here to sanitize the req.Body when working in a production environment 30 | 31 | buf.ReadFrom(req.Body) 32 | 33 | paymentDetails := buf.String() 34 | 35 | err := redisClient.RPush(ctx, "payments", paymentDetails).Err() 36 | 37 | if err != nil { 38 | fmt.Fprintf(w, err.Error()+"\r\n") 39 | return 40 | } 41 | fmt.Fprintf(w, "Payment details accepted successfully\r\n") 42 | } 43 | -------------------------------------------------------------------------------- /redis/redisPkg/connect.go: -------------------------------------------------------------------------------- 1 | package redisPkg 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/go-redis/redis/v8" 7 | ) 8 | 9 | // var RedisIn *redis.Client 10 | 11 | func InitRedis() *redis.Client { 12 | rdb := redis.NewClient(&redis.Options{ 13 | Addr: "localhost:6379", 14 | Password: "", // no password set 15 | DB: 0, // use default DB 16 | }) 17 | 18 | err := rdb.Ping(context.Background()).Err() 19 | if err != nil { 20 | panic(err) 21 | } 22 | 23 | return rdb 24 | } 25 | -------------------------------------------------------------------------------- /redis/test.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/redis/test.txt -------------------------------------------------------------------------------- /redis/test.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/redis/test.yaml -------------------------------------------------------------------------------- /terraform/README.md: -------------------------------------------------------------------------------- 1 | # start project 2 | - step deploy 3 | ```tf 4 | terraform init 5 | terraform plan 6 | terraform apply 7 | ``` -------------------------------------------------------------------------------- /terraform/apps/.gitignore: -------------------------------------------------------------------------------- 1 | .terraform* -------------------------------------------------------------------------------- /terraform/apps/apigw/README.md: -------------------------------------------------------------------------------- 1 | # terraform integration aws-apigw -------------------------------------------------------------------------------- /terraform/apps/apigw/apikey.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_api_key" "tfAPIKey" { 2 | name = "tf-api-key" 3 | description = "terrafrom description" 4 | enabled = true 5 | value = "2af0b7bc-4375-4df2-a44d-2eff998a1193" 6 | } 7 | 8 | resource "aws_api_gateway_usage_plan_key" "tfUsagePlanKey" { 9 | key_id = aws_api_gateway_api_key.tfAPIKey.id 10 | key_type = "API_KEY" 11 | usage_plan_id = aws_api_gateway_usage_plan.tfUsagePlan.id 12 | } -------------------------------------------------------------------------------- /terraform/apps/apigw/deployment.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_deployment" "tfdeployment" { 2 | rest_api_id = aws_api_gateway_rest_api.tfapi.id 3 | 4 | # triggers = { 5 | # redeployment = sha1(jsonencode(aws_api_gateway_rest_api.tfapi.body)) 6 | # } 7 | 8 | lifecycle { 9 | create_before_destroy = true 10 | } 11 | } 12 | 13 | # resource "aws_api_gateway_stage" "example" { 14 | # deployment_id = aws_api_gateway_deployment.example.id 15 | # rest_api_id = aws_api_gateway_rest_api.example.id 16 | # stage_name = "example" 17 | # } 18 | -------------------------------------------------------------------------------- /terraform/apps/apigw/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.4.6" 3 | } 4 | 5 | provider "aws" { 6 | access_key = "" 7 | secret_key = "" 8 | region = var.aws_region 9 | token = "" 10 | endpoints { 11 | sts = "" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /terraform/apps/apigw/resource.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_resource" "tfdms" { 2 | rest_api_id = aws_api_gateway_rest_api.tfapi.id 3 | parent_id = aws_api_gateway_rest_api.tfapi.root_resource_id 4 | path_part = "dms" 5 | } 6 | 7 | resource "aws_api_gateway_resource" "tfdmsv1" { 8 | rest_api_id = aws_api_gateway_rest_api.tfapi.id 9 | parent_id = aws_api_gateway_resource.tfdms.id 10 | path_part = "v1" 11 | } 12 | 13 | resource "aws_api_gateway_resource" "tfdmsv1vendor" { 14 | rest_api_id = aws_api_gateway_rest_api.tfapi.id 15 | parent_id = aws_api_gateway_resource.tfdmsv1.id 16 | path_part = "vendor" 17 | } 18 | 19 | resource "aws_api_gateway_resource" "tfdmsv1vendorlogin" { 20 | rest_api_id = aws_api_gateway_rest_api.tfapi.id 21 | parent_id = aws_api_gateway_resource.tfdmsv1vendor.id 22 | path_part = "login" 23 | } 24 | -------------------------------------------------------------------------------- /terraform/apps/apigw/stage.tf: -------------------------------------------------------------------------------- 1 | # 2 | # Stage and Stage Settings 3 | # 4 | 5 | resource "aws_api_gateway_stage" "dev" { 6 | deployment_id = aws_api_gateway_deployment.tfdeployment.id 7 | rest_api_id = aws_api_gateway_rest_api.tfapi.id 8 | stage_name = "dev" 9 | } 10 | 11 | #resource "aws_api_gateway_stage" "uat" { 12 | # deployment_id = aws_api_gateway_deployment.example.id 13 | # rest_api_id = aws_api_gateway_rest_api.tfapi.id 14 | # stage_name = "uat" 15 | #} 16 | 17 | # resource "aws_api_gateway_stage" "prod" { 18 | # deployment_id = aws_api_gateway_deployment.example.id 19 | # rest_api_id = aws_api_gateway_rest_api.example.id 20 | # stage_name = "prod" 21 | #} 22 | 23 | 24 | 25 | 26 | # resource "aws_api_gateway_method_settings" "example" { 27 | # rest_api_id = aws_api_gateway_rest_api.example.id 28 | # stage_name = aws_api_gateway_stage.example.stage_name 29 | # method_path = "*/*" 30 | 31 | # settings { 32 | # metrics_enabled = true 33 | # } 34 | # } -------------------------------------------------------------------------------- /terraform/apps/apigw/usageplan.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_usage_plan" "tfUsagePlan" { 2 | name = "tf-my-usage-plan" 3 | description = "my description" 4 | product_code = "MYCODE" 5 | 6 | api_stages { 7 | api_id = aws_api_gateway_rest_api.tfapi.id 8 | stage = aws_api_gateway_stage.dev.stage_name 9 | } 10 | 11 | # api_stages { 12 | # api_id = aws_api_gateway_rest_api.example.id 13 | # stage = aws_api_gateway_stage.production.stage_name 14 | # } 15 | 16 | quota_settings { 17 | limit = 20 18 | # offset = 2 19 | period = "DAY" // WEEK 20 | } 21 | 22 | throttle_settings { 23 | burst_limit = 5 24 | rate_limit = 10 25 | } 26 | } -------------------------------------------------------------------------------- /terraform/apps/apigw/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | default = "ap-southeast-1" 3 | description = "AWS Region to deploy example API Gateway REST API" 4 | type = string 5 | } 6 | variable "rest_api_name" { 7 | default = "tf-study-api-gateway-rest-api" 8 | description = "Name of the API Gateway REST API (can be used to trigger redeployments)" 9 | type = string 10 | } 11 | 12 | variable "rest_api_path" { 13 | default = "/path1" 14 | description = "Path to create in the API Gateway REST API (can be used to trigger redeployments)" 15 | type = string 16 | } 17 | 18 | variable "rest_api_path_dms" { 19 | default = "/dms/v1/vendor/login" 20 | description = "Path to create in the API Gateway REST API (can be used to trigger redeployments)" 21 | type = string 22 | } 23 | 24 | variable "rest_api_path_dms_1" { 25 | default = "/dms/v1/file/create" 26 | description = "Path to create in the API Gateway REST API (can be used to trigger redeployments)" 27 | type = string 28 | } 29 | -------------------------------------------------------------------------------- /terraform/apps/apigw_thien/apikey.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_api_key" "ekyc_service" { 2 | name = var.api_key_name 3 | description = "ekyc service" 4 | enabled = true 5 | value = var.api_key_value 6 | } 7 | 8 | resource "aws_api_gateway_usage_plan_key" "ekyc_service_key" { 9 | key_id = aws_api_gateway_api_key.ekyc_service.id 10 | key_type = "API_KEY" 11 | usage_plan_id = aws_api_gateway_usage_plan.ekyc_service.id 12 | } -------------------------------------------------------------------------------- /terraform/apps/apigw_thien/integration.tf: -------------------------------------------------------------------------------- 1 | 2 | ## vpc-link integration 3 | resource "aws_api_gateway_integration" "ekyc_service_integration_nlb" { 4 | rest_api_id = var.apigw_id 5 | resource_id = aws_api_gateway_resource.ekyc_dev_v1_get_image.id 6 | http_method = aws_api_gateway_method.get_image.http_method 7 | integration_http_method = "POST" 8 | type = "HTTP" 9 | uri = var.vpc_link_url 10 | connection_type = "VPC_LINK" 11 | connection_id = var.vpc_link_id 12 | } -------------------------------------------------------------------------------- /terraform/apps/apigw_thien/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.4.6" 3 | } 4 | 5 | provider "aws" { 6 | region = var.aws_region 7 | profile = var.profile 8 | token = "" 9 | endpoints { 10 | sts = "" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /terraform/apps/apigw_thien/method.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_method" "get_image" { 2 | rest_api_id = var.apigw_id 3 | resource_id = aws_api_gateway_resource.ekyc_dev_v1_get_image.id 4 | http_method = "POST" 5 | 6 | authorization = "NONE" 7 | api_key_required = true 8 | request_parameters = { 9 | "method.request.header.x-api-key" = true 10 | } 11 | } -------------------------------------------------------------------------------- /terraform/apps/apigw_thien/resource.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_resource" "ekyc_dev" { 2 | rest_api_id = var.apigw_id 3 | parent_id = var.apigw_root_resource 4 | path_part = "ekycservice" 5 | } 6 | 7 | resource "aws_api_gateway_resource" "ekyc_dev_v1" { 8 | rest_api_id = var.apigw_id 9 | parent_id = aws_api_gateway_resource.ekyc_dev.id 10 | path_part = "v1" 11 | } 12 | 13 | resource "aws_api_gateway_resource" "ekyc_dev_v1_get_image" { 14 | rest_api_id = var.apigw_id 15 | parent_id = aws_api_gateway_resource.ekyc_dev_v1.id 16 | path_part = "get-images" 17 | } 18 | -------------------------------------------------------------------------------- /terraform/apps/apigw_thien/usageplan.tf: -------------------------------------------------------------------------------- 1 | resource "aws_api_gateway_usage_plan" "ekyc_service" { 2 | name = "ekyc_service" 3 | description = "" 4 | product_code = "" 5 | 6 | api_stages { 7 | api_id = var.apigw_id 8 | stage = var.apigw_stage 9 | throttle { 10 | path = var.throttle_method_path_image_get 11 | burst_limit = 3 12 | rate_limit = 6 13 | } 14 | } 15 | 16 | quota_settings { 17 | limit = 10000 18 | # offset = 2 19 | period = "DAY" // WEEK 20 | } 21 | 22 | throttle_settings { 23 | burst_limit = 5 24 | rate_limit = 10 25 | } 26 | 27 | 28 | 29 | } -------------------------------------------------------------------------------- /terraform/apps/lambda/main.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/terraform/apps/lambda/main.tf -------------------------------------------------------------------------------- /terraform/apps/sqs/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.4.6" 3 | } 4 | 5 | provider "aws" { 6 | access_key = "" 7 | secret_key = "" 8 | region = var.aws_region 9 | token = "" 10 | endpoints { 11 | sts = "" 12 | } 13 | } -------------------------------------------------------------------------------- /terraform/apps/sqs/queuefifo.tf: -------------------------------------------------------------------------------- 1 | resource "aws_sqs_queue" "terraform_queue" { 2 | name = var.sqs_hdss_name 3 | fifo_queue = true 4 | content_based_deduplication = true 5 | visibility_timeout_seconds = 120 6 | message_retention_seconds = 432000 7 | } -------------------------------------------------------------------------------- /terraform/apps/sqs/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | default = "ap-southeast-1" 3 | description = "AWS Region to deploy sqs" 4 | type = string 5 | } 6 | variable "sqs_hdss_name" { 7 | default = "queue_name.fifo" 8 | description = "sqs name" 9 | type = string 10 | } 11 | -------------------------------------------------------------------------------- /terraform/apps/ssm/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.4.6" 3 | } 4 | 5 | provider "aws" { 6 | access_key = "" 7 | secret_key = "" 8 | region = var.aws_region 9 | } -------------------------------------------------------------------------------- /terraform/apps/ssm/paramter.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ssm_parameter" "parameter_name" { 2 | name = var.parameter_name_key 3 | type = "String" 4 | value = "{\"secretKey\":\"123\",\"url\":\"https://google.com\",\"secretKeyExternal\":\"\",\"headers\":{\"key\":\"9Buvd48nJsMHe\",\"x-api-key\":\"9Buvd48p5p2HJsMHe\",\"Content-Type\":\"application/json\"}}" 5 | } -------------------------------------------------------------------------------- /terraform/apps/ssm/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | default = "ap-southeast-1" 3 | description = "AWS Region to deploy parameter" 4 | type = string 5 | } 6 | 7 | variable "parameter_name_key" { 8 | default = "sm-parameter-apse1-lab-name" 9 | description = "paramter store of name" 10 | type = string 11 | } 12 | -------------------------------------------------------------------------------- /terraform/apps/vpc/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.4.6" 3 | } 4 | 5 | provider "aws" { 6 | access_key = "" 7 | secret_key = "" 8 | region = var.aws_region 9 | } 10 | -------------------------------------------------------------------------------- /terraform/apps/vpc/subnet.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/terraform/apps/vpc/subnet.tf -------------------------------------------------------------------------------- /terraform/apps/vpc/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | default = "ap-southeast-1" 3 | description = "AWS Region to deploy example API Gateway REST API" 4 | type = string 5 | } 6 | -------------------------------------------------------------------------------- /terraform/apps/vpc/vpc.tf: -------------------------------------------------------------------------------- 1 | resource "aws_lb" "example" { 2 | name = "example" 3 | internal = true 4 | load_balancer_type = "network" 5 | 6 | subnet_mapping { 7 | subnet_id = "subnet-0f622c64f995415d9" 8 | } 9 | } 10 | 11 | resource "aws_api_gateway_vpc_link" "tfvpclink" { 12 | name = "tfvpclink" 13 | description = "terraform create vpc link" 14 | target_arns = [aws_lb.example.arn] 15 | } 16 | 17 | # resource "aws_default_vpc" "default" { 18 | # tags = { 19 | # Name = "Default VPC" 20 | # } 21 | # } 22 | # resource "aws_subnet" "main" { 23 | # vpc_id = aws_default_vpc.default.id 24 | # # cidr_block = "172.31.0.0/16" 25 | 26 | # tags = { 27 | # Name = "Main" 28 | # } 29 | # } -------------------------------------------------------------------------------- /tls/base64parse/certificate_base64.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/tls/base64parse/certificate_base64.txt -------------------------------------------------------------------------------- /tls/base64parse/file.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDePevzoudwdl2n 3 | j1T99lUoaFa3QckMBYin4zJhFCZ65Xyg6fKUyahglznOXknlmwm3qyFY2fL2Cug+ 4 | 4wxyFocTMllMY5H4279ofAS+DuKP9DWL75EbakoPUhebbt4pBlv1OxuYjlHq17f0 5 | wv8PXlw4Zul7mwtl4HemwYmAhSgSiddE72ApF4cD6Wpulryyy2p0FQIIRHpBJae0 6 | 5ubpwgdgT*****wtmWykYeeP5AoGBAMNI 7 | Ae9N4WygEJrfdVzKSvZcyOuNoN5B3YQnvkPb1eoSP3IlfpyO9P1TFrFKlu4rVCgg 8 | RAKlgFl25gl87Xfv0gXZdycImIS2XDmb2voefpG/f88kwqpbSf67VuwmXg3yDclK 9 | yZTBTpTgyv4bXcbXWlMIwpbybGhEkfchcexyPKQxAoGBAKVqPZmWHty4Tz7d0KYK 10 | BzkdOQf5sZb88u1mrIFli4fLQVnQMeHAnwrEfBsl+NSmLmLpicURUYTsX8wUzJ+n 11 | 7ZpbxwZ2qnYYoC9DhwTq7/KaioKncMzlXALZBg1pExUJwhGaVB711cXfRyeW+Ngo 12 | k849ma89xEZNX0meoINPZ73m 13 | -----END PRIVATE KEY----- 14 | -------------------------------------------------------------------------------- /tls/base64parse/file.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEgzCCAmsCAQEwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVk4xDDAKBgNV 3 | BAgMA0hDTTERMA8GA1UEBwwIUGh1Tmh1YW4xDDAKBgNVBAoMA0hEQjELMAkGA1UE 4 | CwwCSVQxDDAKBgNVBAMMA0FQSTEjMCEGCSqGSIb3DQEJARYUaGRiYXBpQGhkYmFu 5 | ay5jb20udm4wHhcNZDUtb9G+Cj4cEWzGgtd4bWDwH 6 | mDSMIfaWWyxRJ7vj4Qvfqx31QjD1xpCegc3l6saeHFNy93LSvJOHO2s1w0WIOtAc 7 | 3u+fruKhRMTgPEgetZVrrqRxuq/G/heHN08H7iRsHjG0cRAHK2daCqNhfx+bZi7z 8 | bUll2H4gYqt/LGYy+MtJlr4FqnQZkS6UdXpBJgGT91JhhKfv8cwGhsobmoSbTVn+ 9 | N+p0Yrz5YtMN48VUqfsz/4CRuseERv1orDszzohqq96jpqYqlFxcguj8twApgCMJ 10 | XwQya13vyTrfTPkAJRz9hSS1tMTUJKVfQIAufb18ft8YOWbdSbNY3LqirDuDqIiu 11 | mXm+K4CMAUFAgxrT2TJC+cua6PQA+OU9t9stA/GuuKf6n3hNum84eaGtfhq1ET3g 12 | RDmPTXuPDTMAhb10txhpOOa7Qz81ZgpkgUirI9Jh+CyvuTtczCfJ20wdsr3KHgoN 13 | uz9CpOvlyg== 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /usecase/blake/2b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "golang.org/x/crypto/blake2b" 5 | ) 6 | 7 | // NewBlake2b256 ... 8 | func NewBlake2b256(data []byte) []byte { 9 | hash := blake2b.Sum256(data) 10 | return hash[:] 11 | } 12 | 13 | // NewBlake2b512 ... 14 | func NewBlake2b512(data []byte) []byte { 15 | hash := blake2b.Sum512(data) 16 | return hash[:] 17 | } 18 | -------------------------------------------------------------------------------- /usecase/blake/2s.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // // NewBlake2b128 ... 4 | // func NewBlake2s128(data []byte) []byte { 5 | // hash, _ := blake2s.New128(data) 6 | // return hash.Sum(nil) 7 | // } 8 | -------------------------------------------------------------------------------- /usecase/blake/2s_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestNewBlake2s128(t *testing.T) { 10 | for i, tt := range []struct { 11 | in []byte 12 | out string 13 | }{ 14 | {[]byte(""), "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8"}, 15 | {[]byte("abc"), "bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319"}, 16 | {[]byte("hello"), "324dcf027dd4a30a932c441f365a25e86b173defa4b8e58948253471b81b72cf"}, 17 | } { 18 | t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { 19 | result := NewBlake2s128(tt.in) 20 | if hex.EncodeToString(result) != tt.out { 21 | t.Errorf("want %v; got %v", tt.out, hex.EncodeToString(result)) 22 | } 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /usecase/blake/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | 7 | "golang.org/x/crypto/blake2s" 8 | ) 9 | 10 | func main() { 11 | out := NewBlake2s128([]byte("abc")) 12 | fmt.Println(hex.EncodeToString(out)) 13 | } 14 | 15 | // NewBlake2b128 ... 16 | func NewBlake2s128(data []byte) []byte { 17 | hash, _ := blake2s.New128(data) 18 | return hash.Sum(nil) 19 | } 20 | -------------------------------------------------------------------------------- /usecase/context/cancel.go: -------------------------------------------------------------------------------- 1 | package main 2 | -------------------------------------------------------------------------------- /usecase/context/deadline.go: -------------------------------------------------------------------------------- 1 | package main 2 | -------------------------------------------------------------------------------- /usecase/context/package.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | func main() { 6 | var wg sync.WaitGroup 7 | done := make(chan interface{}) 8 | defer close(done) 9 | 10 | wg.Add(1) 11 | go func() { 12 | defer wg.Done() 13 | }() 14 | 15 | wg.Add(1) 16 | go func() { 17 | defer wg.Done() 18 | }() 19 | 20 | wg.Wait() 21 | } 22 | -------------------------------------------------------------------------------- /usecase/context/timeout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func TimeOut() { 10 | ctx := context.Background() 11 | ctx, cancel := context.WithTimeout(ctx, time.Duration(time.Second)*2) 12 | defer cancel() 13 | var ( 14 | value string 15 | ) 16 | go func() { 17 | value = handle(ctx) 18 | }() 19 | select { 20 | case <-ctx.Done(): 21 | fmt.Println(ctx.Err()) 22 | fmt.Println("cancelling...") 23 | return 24 | } 25 | fmt.Println("value:", value) 26 | } 27 | 28 | func handle(ctx context.Context) string { 29 | for i := 0; i < 3; i++ { 30 | fmt.Println(i) 31 | } 32 | return "ok" 33 | } 34 | -------------------------------------------------------------------------------- /usecase/crypto-demo/README.md: -------------------------------------------------------------------------------- 1 | # demo crypto 2 | - use algorith basic and most commonly 3 | ## code example: 4 | - sha1: 5 | ```go 6 | func NewSha1(data []byte) []byte { 7 | s := sha1.New() 8 | s.Write(data) 9 | return s.Sum(nil) 10 | } 11 | ``` 12 | - sha256 13 | ```go 14 | func NewSha256(data []byte) []byte { 15 | s := sha256.New() 16 | s.Write(data) 17 | return s.Sum(nil) 18 | } 19 | ``` 20 | - md5 21 | ```go 22 | func Md5test(str string) []byte { 23 | h := md5.New() 24 | io.WriteString(h, str) 25 | return h.Sum(nil) 26 | } 27 | ``` 28 | - blake 29 | ```go 30 | // NewBlake2s128 ... 31 | func NewBlake2s128(data []byte) []byte { 32 | hash, _ := blake2s.New128(data) 33 | return hash.Sum(nil) 34 | } 35 | 36 | // NewBlake2s128 ... 37 | func NewBlake2s256(data []byte) []byte { 38 | hash, _ := blake2s.New256(data) 39 | return hash.Sum(nil) 40 | } 41 | // NewBlake2b256 ... 42 | func NewBlake2b256(data []byte) []byte { 43 | hash := blake2b.Sum256(data) 44 | return hash[:] 45 | } 46 | 47 | // NewBlake2b512 ... 48 | func NewBlake2b512(data []byte) []byte { 49 | hash := blake2b.Sum512(data) 50 | return hash[:] 51 | } 52 | ``` -------------------------------------------------------------------------------- /usecase/crypto-demo/blake/2b.go: -------------------------------------------------------------------------------- 1 | package cryptodemo 2 | 3 | import ( 4 | "golang.org/x/crypto/blake2b" 5 | ) 6 | 7 | // NewBlake2b256 ... 8 | func NewBlake2b256(data []byte) []byte { 9 | hash := blake2b.Sum256(data) 10 | return hash[:] 11 | } 12 | 13 | // NewBlake2b512 ... 14 | func NewBlake2b512(data []byte) []byte { 15 | hash := blake2b.Sum512(data) 16 | return hash[:] 17 | } 18 | -------------------------------------------------------------------------------- /usecase/crypto-demo/blake/2s.go: -------------------------------------------------------------------------------- 1 | package cryptodemo 2 | 3 | import "golang.org/x/crypto/blake2s" 4 | 5 | // NewBlake2s128 ... 6 | func NewBlake2s128(data []byte) []byte { 7 | hash, _ := blake2s.New128(data) 8 | return hash.Sum(nil) 9 | } 10 | 11 | // NewBlake2s128 ... 12 | func NewBlake2s256(data []byte) []byte { 13 | hash, _ := blake2s.New256(data) 14 | return hash.Sum(nil) 15 | } 16 | -------------------------------------------------------------------------------- /usecase/crypto-demo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | cryptodemo "open-dev/usecase/crypto-demo/blake" 7 | cryptodemomd5 "open-dev/usecase/crypto-demo/md5" 8 | cryptodemosha "open-dev/usecase/crypto-demo/sha" 9 | ) 10 | 11 | func main() { 12 | str := "abc" 13 | fmt.Println("md5:", hex.EncodeToString(cryptodemomd5.Md5test(str))) 14 | fmt.Println("blake2s-128:", hex.EncodeToString(cryptodemo.NewBlake2s128([]byte(str)))) 15 | fmt.Println("blake2s-256:", hex.EncodeToString(cryptodemo.NewBlake2s256([]byte(str)))) 16 | fmt.Println("blake2b-256:", hex.EncodeToString(cryptodemo.NewBlake2b256([]byte(str)))) 17 | 18 | fmt.Println("sha-1:", hex.EncodeToString(cryptodemosha.NewSha1([]byte(str)))) 19 | fmt.Println("sha-256:", hex.EncodeToString(cryptodemosha.NewSha256([]byte(str)))) 20 | } 21 | -------------------------------------------------------------------------------- /usecase/crypto-demo/md5/md5.go: -------------------------------------------------------------------------------- 1 | package cryptodemo 2 | 3 | import ( 4 | "crypto/md5" 5 | "io" 6 | ) 7 | 8 | func Md5test(str string) []byte { 9 | h := md5.New() 10 | io.WriteString(h, str) 11 | return h.Sum(nil) 12 | } 13 | -------------------------------------------------------------------------------- /usecase/crypto-demo/sha/sha1.go: -------------------------------------------------------------------------------- 1 | package cryptodemo 2 | 3 | import ( 4 | "crypto/sha1" 5 | "crypto/sha256" 6 | ) 7 | 8 | func NewSha1(data []byte) []byte { 9 | s := sha1.New() 10 | s.Write(data) 11 | return s.Sum(nil) 12 | } 13 | 14 | func NewSha256(data []byte) []byte { 15 | s := sha256.New() 16 | s.Write(data) 17 | return s.Sum(nil) 18 | } 19 | -------------------------------------------------------------------------------- /usecase/defer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Person struct { 6 | Name string 7 | } 8 | 9 | // C1 10 | func main() { 11 | p := Person{} 12 | defer func() { 13 | fmt.Printf("name: %s \n", p.Name) 14 | }() 15 | p.Name = "Opendev" 16 | } 17 | 18 | // C2 19 | func main1() { 20 | p := Person{} 21 | defer fmt.Printf("name %s \n", p.Name) 22 | p.Name = "Opendev" 23 | } 24 | -------------------------------------------------------------------------------- /usecase/environment/dev.yaml: -------------------------------------------------------------------------------- 1 | env_string: "value env string develop" 2 | env_number: 222 -------------------------------------------------------------------------------- /usecase/environment/prod.yaml: -------------------------------------------------------------------------------- 1 | env_string: "value env string production" 2 | env_number: 111 -------------------------------------------------------------------------------- /usecase/function/README.md: -------------------------------------------------------------------------------- 1 | # function 2 | ## variadic function 3 | ### khái niệm: 4 | - variadic function là một function mà cho phép truyền vào bất kì một số nào 5 | - trong golang, để có thể truyền variadic function ta có thể dùng `...` 6 | - xem ví dụ: 7 | ```go 8 | func total(numbers ...int) { 9 | fmt.Print(numbers, " ") 10 | total := 0 11 | for _, num := range numbers { 12 | total += num 13 | } 14 | fmt.Println(total) 15 | } 16 | ``` 17 | 18 | ### calling: 19 | - để call func total ta dùng: 20 | ``` 21 | total(1, 2, 3) => output: 6 22 | total(1) => output: 1 23 | ``` 24 | - hoặc nếu bạn muốn truyền vào func một slice, 25 | ``` 26 | numbers := []int{1, 2, 3, 4} 27 | total(numbers...) 28 | => output: 10 29 | ``` 30 | - call variadic function với một tham số khác. 31 | ``` 32 | func prNumber(sep string, numbers ...int) { 33 | for i, number := range numbers { 34 | if i > 0 { 35 | fmt.Print(sep) 36 | } 37 | fmt.Print(number) 38 | } 39 | } 40 | prNumber(",", 11, 22, 33) 41 | => output: 11,22,33 42 | ``` -------------------------------------------------------------------------------- /usecase/function/variadic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func total(numbers ...int) { 6 | fmt.Print(numbers, " ") 7 | total := 0 8 | for _, num := range numbers { 9 | total += num 10 | } 11 | fmt.Println(total) 12 | } 13 | 14 | func main() { 15 | numbers := []int{1, 2, 3, 4} 16 | total(numbers...) 17 | total(1, 2, 3) 18 | 19 | prNumber(",", 11, 22, 33) 20 | } 21 | 22 | func prNumber(sep string, numbers ...int) { 23 | for i, number := range numbers { 24 | if i > 0 { 25 | fmt.Print(sep) 26 | } 27 | fmt.Print(number) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /usecase/images/resizeImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/images/resizeImage.png -------------------------------------------------------------------------------- /usecase/json/README.md: -------------------------------------------------------------------------------- 1 | # golang read and write file json data 2 | ## read json -------------------------------------------------------------------------------- /usecase/jwttoken/README.md: -------------------------------------------------------------------------------- 1 | # jwt 2 | 3 | ## parse token 4 | - input token data: eyJraWQ******o10SMU_Zw1un3Q 5 | - output: 6 | - header: 7 | ```json 8 | { 9 | "kid": "YuyXoY", 10 | "alg": "RS256" 11 | } 12 | ``` 13 | - payload: 14 | ```json 15 | { 16 | "iss": "https://appleid.apple.com", 17 | "aud": "com.tn.vpn.test", 18 | "exp": 16****606, 19 | "iat": 16****06, 20 | "sub": "000830.b5269f****6b552f.0130", 21 | "nonce": "fb5753a43f4****e14ed875d", 22 | "c_hash": "84eg****EiQ", 23 | "email": "q****v@pr****ay.appleid.com", 24 | "email_verified": "true", 25 | "is_private_email": "true", 26 | "auth_time": 16****06, 27 | "nonce_supported": true 28 | } 29 | ``` 30 | - code: 31 | ```go 32 | func ParserToken(tokenString string) { 33 | claims := jwt.MapClaims{} 34 | token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { 35 | return []byte(""), nil 36 | }) 37 | // ... error handling 38 | fmt.Println(token, err) 39 | // do something with decoded claims 40 | for key, val := range claims { 41 | fmt.Printf("Key: %v, value: %v\n", key, val) 42 | } 43 | } 44 | ``` -------------------------------------------------------------------------------- /usecase/jwttoken/parse.go: -------------------------------------------------------------------------------- 1 | package jwttoken 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/golang-jwt/jwt" 7 | ) 8 | 9 | func ParserToken(tokenString string) { 10 | claims := jwt.MapClaims{} 11 | token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { 12 | return []byte(""), nil 13 | }) 14 | // ... error handling 15 | fmt.Println(token, err) 16 | // do something with decoded claims 17 | for key, val := range claims { 18 | fmt.Printf("Key: %v, value: %v\n", key, val) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /usecase/map/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/.DS_Store -------------------------------------------------------------------------------- /usecase/map/p1/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestMap(t *testing.T) { 9 | cache := NewCache() 10 | for i := 0; i <= 100; i++ { 11 | // go func(i int) { 12 | cache.Set(fmt.Sprint(i), i) 13 | // }(i) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /usecase/map/p1/map_1.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p1/map_1.1.png -------------------------------------------------------------------------------- /usecase/map/p1/map_1.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p1/map_1.2.png -------------------------------------------------------------------------------- /usecase/map/p2/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestMap(t *testing.T) { 9 | cache := NewCache() 10 | for i := 0; i <= 100; i++ { 11 | go func(i int) { 12 | cache.Set(fmt.Sprint(i), i) 13 | }(i) 14 | } 15 | } 16 | 17 | // go test ./... -v --race 18 | -------------------------------------------------------------------------------- /usecase/map/p2/map_2.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p2/map_2.1.png -------------------------------------------------------------------------------- /usecase/map/p2/map_2.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p2/map_2.2.png -------------------------------------------------------------------------------- /usecase/map/p2/map_2.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p2/map_2.3.png -------------------------------------------------------------------------------- /usecase/map/p3/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestMap(t *testing.T) { 9 | cache := NewCacheIndex(10) 10 | for i := 0; i <= 100; i++ { 11 | go func(i int) { 12 | cache.Set(fmt.Sprint(i), i) 13 | }(i) 14 | } 15 | } 16 | 17 | // go test ./... -v --race 18 | -------------------------------------------------------------------------------- /usecase/map/p3/map_3.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p3/map_3.1.png -------------------------------------------------------------------------------- /usecase/map/p3/map_3.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p3/map_3.2.png -------------------------------------------------------------------------------- /usecase/map/p3/map_3.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p3/map_3.3.png -------------------------------------------------------------------------------- /usecase/map/p3/map_3.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p3/map_3.4.png -------------------------------------------------------------------------------- /usecase/map/p4/golang_map_4.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p4/golang_map_4.1.png -------------------------------------------------------------------------------- /usecase/map/p4/golang_map_4.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p4/golang_map_4.2.png -------------------------------------------------------------------------------- /usecase/map/p4/golang_map_4.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/map/p4/golang_map_4.3.png -------------------------------------------------------------------------------- /usecase/password/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "io" 7 | "math/big" 8 | ) 9 | 10 | // Generate returns a newly generated password 11 | func Generate(minLength int, maxLength int) string { 12 | var chars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+,.?/:;{}[]`~") 13 | 14 | length, err := rand.Int(rand.Reader, big.NewInt(int64(maxLength-minLength))) 15 | if err != nil { 16 | panic(err) // handle this gracefully 17 | } 18 | length.Add(length, big.NewInt(int64(minLength))) 19 | 20 | intLength := int(length.Int64()) 21 | 22 | newPassword := make([]byte, intLength) 23 | randomData := make([]byte, intLength+intLength/4) 24 | charLen := byte(len(chars)) 25 | maxrb := byte(256 - (256 % len(chars))) 26 | i := 0 27 | for { 28 | if _, err := io.ReadFull(rand.Reader, randomData); err != nil { 29 | panic(err) 30 | } 31 | for _, c := range randomData { 32 | if c >= maxrb { 33 | continue 34 | } 35 | newPassword[i] = chars[c%charLen] 36 | i++ 37 | if i == intLength { 38 | return string(newPassword) 39 | } 40 | } 41 | } 42 | } 43 | 44 | func main() { 45 | pw := Generate(10, 15) 46 | fmt.Printf("password: %s", pw) 47 | } 48 | -------------------------------------------------------------------------------- /usecase/resizeImage.go: -------------------------------------------------------------------------------- 1 | package usecase 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/base64" 7 | "fmt" 8 | "image" 9 | 10 | "github.com/disintegration/imaging" 11 | ) 12 | 13 | func ResizeImageFromBase64(imgBase64 string, newHeight int) (string, error) { 14 | // convert image is base64 to byte 15 | unbased, err := base64.StdEncoding.DecodeString(imgBase64) 16 | if err != nil { 17 | return "", fmt.Errorf("cannot decode base64 err=%v", err) 18 | } 19 | 20 | r := bytes.NewReader(unbased) 21 | // use library imaging 22 | // parse reader to image 23 | img, err := imaging.Decode(r) 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | // calculator new width of image 29 | newWidth := newHeight * img.Bounds().Max.X / img.Bounds().Max.Y 30 | 31 | // resize new image 32 | nrgba := imaging.Resize(img, newWidth, newHeight, imaging.Lanczos) 33 | 34 | return toBase64(nrgba) 35 | } 36 | 37 | func toBase64(dst *image.NRGBA) (string, error) { 38 | var b bytes.Buffer 39 | foo := bufio.NewWriter(&b) 40 | if err := imaging.Encode(foo, dst, imaging.JPEG); err != nil { 41 | return "", err 42 | } 43 | return base64.StdEncoding.EncodeToString(b.Bytes()), nil 44 | } 45 | -------------------------------------------------------------------------------- /usecase/time/README.md: -------------------------------------------------------------------------------- 1 | ## debug resource 2 | - run: go tool pprof http://localhost:1234/debug/pprof/goroutine 3 | - run heap: go tool pprof http://localhost:1234/debug/pprof/heap 4 | - run allocs: go tool pprof http://localhost:1234/debug/pprof/allocs 5 | - -------------------------------------------------------------------------------- /usecase/time/profile001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/profile001.png -------------------------------------------------------------------------------- /usecase/time/profile002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/profile002.png -------------------------------------------------------------------------------- /usecase/time/profile003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/profile003.png -------------------------------------------------------------------------------- /usecase/time/profile004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/profile004.png -------------------------------------------------------------------------------- /usecase/time/profile005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/profile005.png -------------------------------------------------------------------------------- /usecase/time/profile006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/profile006.png -------------------------------------------------------------------------------- /usecase/time/profile007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/profile007.png -------------------------------------------------------------------------------- /usecase/time/tick.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | type Event struct { 15 | } 16 | 17 | func handle(e Event) { 18 | fmt.Println(e) 19 | } 20 | func main() { 21 | go consumer(make(<-chan Event)) 22 | // wait 23 | go func() { 24 | http.ListenAndServe(":1234", nil) 25 | }() 26 | sigs := make(chan os.Signal, 1) 27 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 28 | done := make(chan bool, 1) 29 | go func() { 30 | <-sigs 31 | done <- true 32 | }() 33 | <-done 34 | } 35 | 36 | // bad solution: 37 | func consumer(ch <-chan Event) { 38 | fmt.Println("start consumer") 39 | for { 40 | select { 41 | case event := <-ch: 42 | handle(event) 43 | case <-time.After(time.Nanosecond): 44 | log.Println("warning: no messages received") 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /usecase/time/tickHour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/tickHour.png -------------------------------------------------------------------------------- /usecase/time/tickNano-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/tickNano-2.png -------------------------------------------------------------------------------- /usecase/time/tickNano-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/tickNano-3.png -------------------------------------------------------------------------------- /usecase/time/tickNano-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/tickNano-4.png -------------------------------------------------------------------------------- /usecase/time/tickNano.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/tickNano.png -------------------------------------------------------------------------------- /usecase/time/tickSecond-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/tickSecond-2.png -------------------------------------------------------------------------------- /usecase/time/tickSecond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducnpdev/open-dev/7c4eb6a814bc54d33a6c02251129eeaed16b914a/usecase/time/tickSecond.png --------------------------------------------------------------------------------