This is an automated email from the ASF dual-hosted git repository.

zfeng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-seata-go-samples.git


The following commit(s) were added to refs/heads/main by this push:
     new bb42752  Saga end-to-end verification (#74)
bb42752 is described below

commit bb42752c5f27e065e488cfbeb077cbe1e06b31a7
Author: FengZhang <[email protected]>
AuthorDate: Sun Dec 21 19:53:08 2025 +0800

    Saga end-to-end verification (#74)
    
    * support saga e2e test
    
    * fix go mod
    
    * fix gomod
    
    * fix err
    
    * update go version for readme
    
    * fix import sdk
    
    * fix version err
    
    * remove json
---
 .licenserc.yaml                                    |   1 +
 README.md                                          |   2 +-
 at/grpc/cmd/client/main.go                         |   1 +
 at/grpc/cmd/server/main.go                         |   3 +-
 at/grpc/pb/at_grpc.pb.go                           |   5 +-
 at/grpc/pb/at_grpc_grpc.pb.go                      |   1 +
 at/grpc/service/service.go                         |   5 +-
 go.mod                                             |  46 +--
 go.sum                                             |  75 +++--
 goimports.sh                                       |   8 +-
 saga/e2e/Makefile                                  |  33 +++
 saga/e2e/README.md                                 | 126 ++++++++
 saga/e2e/README_zh.md                              | 134 +++++++++
 goimports.sh => saga/e2e/config.yaml               |  28 +-
 saga/e2e/dbcheck/main.go                           | 329 +++++++++++++++++++++
 goimports.sh => saga/e2e/docker-compose.yml        |  36 ++-
 saga/e2e/main.go                                   | 291 ++++++++++++++++++
 goimports.sh => saga/e2e/migrate.sh                |  21 +-
 goimports.sh => saga/e2e/run.sh                    |  18 +-
 saga/e2e/run_all.sh                                | 144 +++++++++
 goimports.sh => saga/e2e/run_compensation.sh       |  21 +-
 saga/e2e/scenario_comp_balance.go                  |  29 ++
 saga/e2e/scenario_comp_inventory.go                |  29 ++
 saga/e2e/scenario_success.go                       |  28 ++
 goimports.sh => saga/e2e/seatago.yaml              |  40 ++-
 saga/e2e/sql/mysql_saga_schema.sql                 |  78 +++++
 .../statelang/reduce_inventory_and_balance.json    |  75 +++++
 goimports.sh => saga/e2e/up_and_run.sh             |  30 +-
 28 files changed, 1495 insertions(+), 142 deletions(-)

diff --git a/.licenserc.yaml b/.licenserc.yaml
index 46b83c6..16ee2ba 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -60,6 +60,7 @@ header: # `header` section is configurations for source codes 
license header.
     - 'DISCLAIMER'
     - 'NOTICE'
     - '.github'
+    - '**/*.json'
   comment: on-failure
 
   language:
diff --git a/README.md b/README.md
index dc28f50..34bad31 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ go work use ./seata-go-samples
 Now, the content of go.work file is as follows.
 
 ```text
-go 1.19
+go 1.20
 
 use (
         ./seata-go
diff --git a/at/grpc/cmd/client/main.go b/at/grpc/cmd/client/main.go
index 0558db4..f168e20 100644
--- a/at/grpc/cmd/client/main.go
+++ b/at/grpc/cmd/client/main.go
@@ -21,6 +21,7 @@ package main
 import (
        "context"
        "flag"
+
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
        __ "seata.apache.org/seata-go-samples/at/grpc/pb"
diff --git a/at/grpc/cmd/server/main.go b/at/grpc/cmd/server/main.go
index d67b597..51046ab 100644
--- a/at/grpc/cmd/server/main.go
+++ b/at/grpc/cmd/server/main.go
@@ -21,7 +21,8 @@ package main
 import (
        "fmt"
        "net"
-       "seata.apache.org/seata-go-samples/at/grpc/pb"
+
+       __ "seata.apache.org/seata-go-samples/at/grpc/pb"
        "seata.apache.org/seata-go/pkg/client"
 
        "google.golang.org/grpc"
diff --git a/at/grpc/pb/at_grpc.pb.go b/at/grpc/pb/at_grpc.pb.go
index c063010..c5837fc 100644
--- a/at/grpc/pb/at_grpc.pb.go
+++ b/at/grpc/pb/at_grpc.pb.go
@@ -23,11 +23,12 @@
 package __
 
 import (
+       reflect "reflect"
+       sync "sync"
+
        protoreflect "google.golang.org/protobuf/reflect/protoreflect"
        protoimpl "google.golang.org/protobuf/runtime/protoimpl"
        wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
-       reflect "reflect"
-       sync "sync"
 )
 
 const (
diff --git a/at/grpc/pb/at_grpc_grpc.pb.go b/at/grpc/pb/at_grpc_grpc.pb.go
index 5c4ee43..c2c9886 100644
--- a/at/grpc/pb/at_grpc_grpc.pb.go
+++ b/at/grpc/pb/at_grpc_grpc.pb.go
@@ -25,6 +25,7 @@ package __
 
 import (
        context "context"
+
        grpc "google.golang.org/grpc"
        codes "google.golang.org/grpc/codes"
        status "google.golang.org/grpc/status"
diff --git a/at/grpc/service/service.go b/at/grpc/service/service.go
index 39077bf..50cc655 100644
--- a/at/grpc/service/service.go
+++ b/at/grpc/service/service.go
@@ -21,10 +21,11 @@ import (
        "context"
        "database/sql"
        "fmt"
-       "google.golang.org/protobuf/types/known/wrapperspb"
-       "seata.apache.org/seata-go-samples/at/grpc/pb"
        "time"
 
+       "google.golang.org/protobuf/types/known/wrapperspb"
+       __ "seata.apache.org/seata-go-samples/at/grpc/pb"
+
        sql2 "seata.apache.org/seata-go/pkg/datasource/sql"
 )
 
diff --git a/go.mod b/go.mod
index 9ffc9dd..c1b7e23 100644
--- a/go.mod
+++ b/go.mod
@@ -7,28 +7,35 @@ require (
        github.com/gin-gonic/gin v1.9.1
        github.com/go-sql-driver/mysql v1.7.0
        github.com/parnurzeal/gorequest v0.2.16
-       google.golang.org/grpc v1.56.3
-       google.golang.org/protobuf v1.30.0
+       google.golang.org/grpc v1.57.0
+       google.golang.org/protobuf v1.31.0
        gorm.io/driver/mysql v1.4.5
        gorm.io/gorm v1.24.3
-       seata.apache.org/seata-go v1.2.1-0.20240604133652-ad092d5eb331
+       seata.apache.org/seata-go v1.2.1-0.20251220113411-b18bcb019b65
 )
 
-// For local testing only.
-//replace seata.apache.org/seata-go => ../seata-go
+require (
+       cloud.google.com/go/compute v1.20.1 // indirect
+       github.com/antlr/antlr4/runtime/Go/antlr/v4 
v4.0.0-20230305170008-8188dc5388df // indirect
+       github.com/google/cel-go v0.18.0 // indirect
+       github.com/robertkrimen/otto v0.4.0 // indirect
+       github.com/stoewer/go-strcase v1.2.0 // indirect
+       golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect
+       google.golang.org/genproto/googleapis/api 
v0.0.0-20230803162519-f966b187b2e5 // indirect
+       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20230803162519-f966b187b2e5 // indirect
+       gopkg.in/sourcemap.v1 v1.0.5 // indirect
+)
 
 require (
-       cloud.google.com/go/compute v1.19.1 // indirect
        cloud.google.com/go/compute/metadata v0.2.3 // indirect
        github.com/RoaringBitmap/roaring v1.2.3 // indirect
        github.com/Workiva/go-datastructures v1.0.52 // indirect
        github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // 
indirect
        github.com/alibaba/sentinel-golang v1.0.4 // indirect
        github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 // indirect
-       github.com/apache/dubbo-getty v1.4.10 // indirect
+       github.com/apache/dubbo-getty v1.5.0 // indirect
        github.com/apache/dubbo-go-hessian2 v1.12.2 // indirect
-       github.com/arana-db/parser v0.2.5 // indirect
-       github.com/benbjohnson/clock v1.1.0 // indirect
+       github.com/arana-db/parser v0.2.17 // indirect
        github.com/beorn7/perks v1.0.1 // indirect
        github.com/bits-and-blooms/bitset v1.2.0 // indirect
        github.com/bluele/gcache v0.0.2 // indirect
@@ -139,24 +146,25 @@ require (
        go.opentelemetry.io/otel/sdk v1.10.0 // indirect
        go.opentelemetry.io/otel/trace v1.11.0 // indirect
        go.uber.org/atomic v1.10.0 // indirect
-       go.uber.org/multierr v1.8.0 // indirect
-       go.uber.org/zap v1.21.0 // indirect
+       go.uber.org/multierr v1.10.0 // indirect
+       go.uber.org/zap v1.27.0 // indirect
        golang.org/x/arch v0.3.0 // indirect
-       golang.org/x/crypto v0.17.0 // indirect
-       golang.org/x/net v0.17.0 // indirect
-       golang.org/x/oauth2 v0.7.0 // indirect
-       golang.org/x/sync v0.1.0 // indirect
-       golang.org/x/sys v0.15.0 // indirect
+       golang.org/x/crypto v0.21.0 // indirect
+       golang.org/x/net v0.23.0 // indirect
+       golang.org/x/oauth2 v0.8.0 // indirect
+       golang.org/x/sync v0.11.0 // indirect
+       golang.org/x/sys v0.25.0 // indirect
        golang.org/x/text v0.14.0 // indirect
        golang.org/x/time v0.1.0 // indirect
        google.golang.org/appengine v1.6.7 // indirect
-       google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // 
indirect
+       google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // 
indirect
        gopkg.in/ini.v1 v1.66.2 // indirect
        gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
        gopkg.in/yaml.v2 v2.4.0 // indirect
-       gopkg.in/yaml.v3 v3.0.1 // indirect
+       gopkg.in/yaml.v3 v3.0.1
        moul.io/http2curl v1.0.0 // indirect
        vimagination.zapto.org/byteio v0.0.0-20200222190125-d27cba0f0b10 // 
indirect
 )
 
-// replace github.com/knadh/koanf => github.com/knadh/koanf/v2 v2.1.2
+// For local testing only.
+//replace seata.apache.org/seata-go => ../incubator-seata-go
diff --git a/go.sum b/go.sum
index 74fd412..837ed75 100644
--- a/go.sum
+++ b/go.sum
@@ -115,8 +115,8 @@ cloud.google.com/go/compute v1.12.0/go.mod 
h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x
 cloud.google.com/go/compute v1.12.1/go.mod 
h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
 cloud.google.com/go/compute v1.13.0/go.mod 
h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE=
 cloud.google.com/go/compute v1.14.0/go.mod 
h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
-cloud.google.com/go/compute v1.19.1 
h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
-cloud.google.com/go/compute v1.19.1/go.mod 
h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
+cloud.google.com/go/compute v1.20.1 
h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
+cloud.google.com/go/compute v1.20.1/go.mod 
h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
 cloud.google.com/go/compute/metadata v0.1.0/go.mod 
h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
 cloud.google.com/go/compute/metadata v0.2.0/go.mod 
h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
 cloud.google.com/go/compute/metadata v0.2.1/go.mod 
h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
@@ -412,7 +412,7 @@ github.com/afex/hystrix-go 
v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vaj
 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod 
h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
 github.com/agiledragon/gomonkey v2.0.2+incompatible 
h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
 github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod 
h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
-github.com/agiledragon/gomonkey/v2 v2.9.0 
h1:PDiKKybR596O6FHW+RVSG0Z7uGCBNbmbUXh3uCNQ7Hc=
+github.com/agiledragon/gomonkey/v2 v2.12.0 
h1:ek0dYu9K1rSV+TgkW5LvNNPRWyDZVIxGMCFI6Pz9o38=
 github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod 
h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod 
h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod 
h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -425,17 +425,19 @@ github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod 
h1:v8ESoHo4SyHmuB4b1tJqDH
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 
h1:PpfENOj/vPfhhy9N2OFRjpue0hjM5XqAp2thFmkXXIk=
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704/go.mod 
h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
 github.com/antihax/optional v1.0.0/go.mod 
h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df 
h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 
v4.0.0-20230305170008-8188dc5388df/go.mod 
h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 github.com/apache/dubbo-getty v1.4.9/go.mod 
h1:6qmrqBSPGs3B35zwEuGhEYNVsx1nfGT/xzV2yOt2amM=
-github.com/apache/dubbo-getty v1.4.10 
h1:ZmkpHJa/qgS0evX2tTNqNCz6rClI/9Wwp7ctyMml82w=
-github.com/apache/dubbo-getty v1.4.10/go.mod 
h1:V64WqLIxksEgNu5aBJBOxNIvpOZyfUJ7J/DXBlKSUoA=
+github.com/apache/dubbo-getty v1.5.0 
h1:40RMjEoSfTuoG5EwKbGgfhjd7m6Zc7qBfOFgQv1jHCI=
+github.com/apache/dubbo-getty v1.5.0/go.mod 
h1:V64WqLIxksEgNu5aBJBOxNIvpOZyfUJ7J/DXBlKSUoA=
 github.com/apache/dubbo-go-hessian2 v1.9.1/go.mod 
h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE=
 github.com/apache/dubbo-go-hessian2 v1.9.3/go.mod 
h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE=
 github.com/apache/dubbo-go-hessian2 v1.12.2 
h1:2/56JRPng2lnLziJF3fqmSgsg28Yt1a5YZ5RX+jHDGs=
 github.com/apache/dubbo-go-hessian2 v1.12.2/go.mod 
h1:QP9Tc0w/B/mDopjusebo/c7GgEfl6Lz8jeuFg8JA6yw=
 github.com/apache/thrift v0.12.0/go.mod 
h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/apache/thrift v0.13.0/go.mod 
h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
-github.com/arana-db/parser v0.2.5 
h1:X7SZUjs52nNkX+PL3wrJVd7+BL4VALIXahX+Bx+pmOQ=
-github.com/arana-db/parser v0.2.5/go.mod 
h1:/XA29bplweWSEAjgoM557ZCzhBilSawUlHcZFjOeDAc=
+github.com/arana-db/parser v0.2.17 
h1:4wNfSgza2N3pjpwR5jmWLvu4L6Sme6EtoLuZOgwWlsU=
+github.com/arana-db/parser v0.2.17/go.mod 
h1:/XA29bplweWSEAjgoM557ZCzhBilSawUlHcZFjOeDAc=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod 
h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod 
h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
 github.com/armon/go-metrics v0.3.9/go.mod 
h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
@@ -455,7 +457,6 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url 
v1.3.2/go.mod h1:72H
 github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod 
h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk=
 github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod 
h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g=
 github.com/aws/smithy-go v1.8.0/go.mod 
h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
-github.com/benbjohnson/clock v1.1.0 
h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
 github.com/benbjohnson/clock v1.1.0/go.mod 
h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod 
h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod 
h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -737,6 +738,8 @@ github.com/gonum/stat 
v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4g
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod 
h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod 
h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/cel-go v0.18.0 
h1:u74MPiEC8mejBrkXqrTWT102g5IFEUjxOngzQIijMzU=
+github.com/google/cel-go v0.18.0/go.mod 
h1:PVAybmSnWkNMUZR/tEWFUiJ1Np4Hz0MHsZJcgC4zln4=
 github.com/google/go-cmp v0.2.0/go.mod 
h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod 
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod 
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -1008,6 +1011,7 @@ github.com/mattn/go-isatty v0.0.16/go.mod 
h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
 github.com/mattn/go-isatty v0.0.19 
h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
 github.com/mattn/go-isatty v0.0.19/go.mod 
h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.2/go.mod 
h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-sqlite3 v1.14.19 
h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod 
h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.4 
h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
 github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod 
h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
@@ -1188,6 +1192,8 @@ github.com/rcrowley/go-metrics 
v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn
 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod 
h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod 
h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 github.com/rhnvrm/simples3 v0.6.1/go.mod 
h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
+github.com/robertkrimen/otto v0.4.0 
h1:/c0GRrK1XDPcgIasAsnlpBT5DelIeB9U/Z/JCQsgr7E=
+github.com/robertkrimen/otto v0.4.0/go.mod 
h1:uW9yN1CYflmUQYvAMS0m+ZiNo3dMzRUDQJX0jWbzgxw=
 github.com/robfig/cron/v3 v3.0.1 
h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
 github.com/robfig/cron/v3 v3.0.1/go.mod 
h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod 
h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -1241,6 +1247,8 @@ github.com/spf13/pflag v1.0.3/go.mod 
h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.7.0/go.mod 
h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/stoewer/go-strcase v1.2.0 
h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
+github.com/stoewer/go-strcase v1.2.0/go.mod 
h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod 
h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod 
h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod 
h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
@@ -1376,15 +1384,16 @@ go.uber.org/atomic v1.10.0 
h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
 go.uber.org/atomic v1.10.0/go.mod 
h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
 go.uber.org/goleak v1.1.10/go.mod 
h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
 go.uber.org/goleak v1.1.11/go.mod 
h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
 go.uber.org/goleak v1.1.12/go.mod 
h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/multierr v1.1.0/go.mod 
h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/multierr v1.3.0/go.mod 
h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
 go.uber.org/multierr v1.5.0/go.mod 
h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
 go.uber.org/multierr v1.6.0/go.mod 
h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
 go.uber.org/multierr v1.7.0/go.mod 
h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
-go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
 go.uber.org/multierr v1.8.0/go.mod 
h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
+go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
+go.uber.org/multierr v1.10.0/go.mod 
h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod 
h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
 go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
@@ -1394,8 +1403,9 @@ go.uber.org/zap v1.16.0/go.mod 
h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
 go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
 go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
 go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
-go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
 go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod 
h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
 golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -1417,8 +1427,8 @@ golang.org/x/crypto 
v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod 
h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod 
h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.7.0/go.mod 
h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod 
h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.21.0/go.mod 
h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod 
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1433,8 +1443,9 @@ golang.org/x/exp 
v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod 
h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod 
h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod 
h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 
h1:FR+oGxGfbQu1d+jglI3rCkjAjUnhRSZcUxr+DqlDLNo=
 golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod 
h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
+golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 
h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw=
+golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod 
h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod 
h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod 
h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod 
h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -1537,8 +1548,8 @@ golang.org/x/net v0.2.0/go.mod 
h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
+golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod 
h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1565,8 +1576,8 @@ golang.org/x/oauth2 
v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri
 golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod 
h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod 
h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 golang.org/x/oauth2 v0.6.0/go.mod 
h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
-golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
-golang.org/x/oauth2 v0.7.0/go.mod 
h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
+golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
+golang.org/x/oauth2 v0.8.0/go.mod 
h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1581,8 +1592,9 @@ golang.org/x/sync 
v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
+golang.org/x/sync v0.11.0/go.mod 
h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1695,8 +1707,8 @@ golang.org/x/sys v0.2.0/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
+golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod 
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod 
h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -1997,8 +2009,12 @@ google.golang.org/genproto 
v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZV
 google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod 
h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
 google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod 
h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
 google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod 
h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 
h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod 
h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
+google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e 
h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg=
+google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod 
h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108=
+google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 
h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44=
+google.golang.org/genproto/googleapis/api 
v0.0.0-20230803162519-f966b187b2e5/go.mod 
h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 
h1:eSaPbMR4T7WfH9FvABk36NBMacoTUKdWCvV0dx+KfOg=
+google.golang.org/genproto/googleapis/rpc 
v0.0.0-20230803162519-f966b187b2e5/go.mod 
h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I=
 google.golang.org/grpc v1.8.0/go.mod 
h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.14.0/go.mod 
h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.17.0/go.mod 
h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
@@ -2047,8 +2063,8 @@ google.golang.org/grpc v1.50.0/go.mod 
h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD
 google.golang.org/grpc v1.50.1/go.mod 
h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 google.golang.org/grpc v1.51.0/go.mod 
h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
 google.golang.org/grpc v1.52.0/go.mod 
h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
-google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
-google.golang.org/grpc v1.56.3/go.mod 
h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
+google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
+google.golang.org/grpc v1.57.0/go.mod 
h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod 
h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod 
h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod 
h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -2065,8 +2081,9 @@ google.golang.org/protobuf v1.26.0/go.mod 
h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
 google.golang.org/protobuf v1.27.1/go.mod 
h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.28.0/go.mod 
h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.28.1/go.mod 
h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.30.0 
h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
 google.golang.org/protobuf v1.30.0/go.mod 
h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.31.0 
h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod 
h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod 
h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod 
h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -2087,6 +2104,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod 
h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24
 gopkg.in/natefinch/lumberjack.v2 v2.2.1 
h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
 gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod 
h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 gopkg.in/resty.v1 v1.12.0/go.mod 
h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
+gopkg.in/sourcemap.v1 v1.0.5/go.mod 
h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
 gopkg.in/square/go-jose.v2 v2.3.1/go.mod 
h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod 
h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/warnings.v0 v0.1.2/go.mod 
h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
@@ -2137,8 +2156,8 @@ rsc.io/binaryregexp v0.2.0/go.mod 
h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-seata.apache.org/seata-go v1.2.1-0.20240604133652-ad092d5eb331 
h1:Pbes9LbfpN10s/Kt/ioEkqBg4l3hykoZjprZE0VuTdU=
-seata.apache.org/seata-go v1.2.1-0.20240604133652-ad092d5eb331/go.mod 
h1:lzLjtX6x5zXDflnQ53jo/Gsbqu6gu8+ZaaQTHz6inYk=
+seata.apache.org/seata-go v1.2.1-0.20251220113411-b18bcb019b65 
h1:2d4nvNnAnszr4SmSZa1VSLZsnzu2jHR0jtRAPH8R1cI=
+seata.apache.org/seata-go v1.2.1-0.20251220113411-b18bcb019b65/go.mod 
h1:BH35FqKInt+3tV/Wp0OS2HfJswKdgv59MVJJ6KWxirY=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/goimports.sh b/goimports.sh
old mode 100644
new mode 100755
index 4c30f1c..137f786
--- a/goimports.sh
+++ b/goimports.sh
@@ -16,14 +16,14 @@
 #
 
 # format go imports style
-go install -v golang.org/x/tools/cmd/goimports
-goimports  -w .
+go install golang.org/x/tools/cmd/[email protected]
+goimports  -local seata.apache.org/seata-go -w .
 
 # format licence style
-go install github.com/apache/skywalking-eyes/cmd/license-eye@latest
+go install github.com/apache/skywalking-eyes/cmd/[email protected]
 license-eye header fix
 # check dependency licence is valid
 license-eye dependency check
 
 # format go.mod
-go mod tidy
+go mod tidy
\ No newline at end of file
diff --git a/saga/e2e/Makefile b/saga/e2e/Makefile
new file mode 100644
index 0000000..8a38616
--- /dev/null
+++ b/saga/e2e/Makefile
@@ -0,0 +1,33 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.PHONY: run migrate
+
+SEATA_CONF ?= saga/e2e/seatago.yaml
+ENGINE_CONF ?= saga/e2e/config.yaml
+
+run:
+       go run ./saga/e2e -seataConf=$(SEATA_CONF) -engineConf=$(ENGINE_CONF)
+
+# Requires mysql client; provide MYSQL_HOST, MYSQL_PORT, MYSQL_USER, 
MYSQL_PWD, MYSQL_DB
+migrate:
+       @[ -n "$(MYSQL_HOST)" ] || (echo "MYSQL_HOST is required" && exit 1)
+       @[ -n "$(MYSQL_PORT)" ] || (echo "MYSQL_PORT is required" && exit 1)
+       @[ -n "$(MYSQL_USER)" ] || (echo "MYSQL_USER is required" && exit 1)
+       @[ -n "$(MYSQL_PWD)" ] || (echo "MYSQL_PWD is required" && exit 1)
+       @[ -n "$(MYSQL_DB)" ] || (echo "MYSQL_DB is required" && exit 1)
+       @echo "Applying schema to $(MYSQL_HOST):$(MYSQL_PORT)/$(MYSQL_DB)..."
+       @mysql -h"$(MYSQL_HOST)" -P"$(MYSQL_PORT)" -u"$(MYSQL_USER)" 
-p"$(MYSQL_PWD)" "$(MYSQL_DB)" < sql/mysql_saga_schema.sql
+       @echo "Schema applied."
diff --git a/saga/e2e/README.md b/saga/e2e/README.md
new file mode 100644
index 0000000..dfa399c
--- /dev/null
+++ b/saga/e2e/README.md
@@ -0,0 +1,126 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+# Saga E2E (MySQL) — End‑to‑End Guide
+
+Chinese version: see `README_zh.md`.
+
+This example demonstrates a Java‑aligned Seata SAGA flow in Go, with MySQL 
persistence and Seata Server (TC) integration. It includes one‑click scripts, 
DB validation tooling, and compensation semantics compatible with the Java 
reference.
+
+## What it starts
+
+- MySQL 8.0 — stores SAGA state
+- Seata Server 1.6.1 — TC for global coordination
+- A demo SAGA: ReduceInventory → ReduceBalance (with compensations)
+
+## Quick start (one‑click)
+
+Prerequisites: Go 1.20+, Docker, and docker-compose available on the host.
+
+```
+saga/e2e/run_all.sh --up \
+  --seata saga/e2e/seatago.yaml \
+  --engine saga/e2e/config.yaml
+```
+
+## Use local seata-go while developing
+
+The sample repository currently depends on a released version of seata-go. When
+you need to exercise the E2E flow against your working tree at
+`../incubator-seata-go`, temporarily add a replace directive before running:
+
+```
+go mod edit -replace seata.apache.org/seata-go=../incubator-seata-go
+go mod tidy # optional, only if dependencies changed
+```
+
+After the test run, drop the replace so the module file stays clean:
+
+```
+go mod edit -dropreplace seata.apache.org/seata-go
+```
+
+When using Go 1.18+, you can alternatively create a temporary workspace:
+
+```
+go work init
+go work use .
+go work use ../incubator-seata-go
+go run ./saga/e2e
+rm go.work
+```
+
+Knobs:
+- `WAIT_TIMEOUT` (default 60), `WAIT_INTERVAL` (2) TCP readiness
+- `WAIT_READY_MARGIN` (default 15) extra wait after ports are open
+
+Expected:
+- `DB validation (success) OK`
+- `DB validation (compensate-balance) OK`
+- `DB validation (compensate-inventory) OK`
+- `[+] All e2e scenarios finished`
+
+## Configuration
+
+- Seata client (`saga/e2e/seatago.yaml`)
+  - `application-id`, `tx-service-group`
+  - `service.grouplist.default`: `host:port` of Seata Server
+
+- Saga engine (`saga/e2e/config.yaml`)
+  - `store_enabled: true`, `store_type: mysql`
+  - `store_dsn: user:pass@tcp(127.0.0.1:3306)/seata_saga?parseTime=true`
+  - `tc_enabled: true`
+  - `state_machine_resources: [saga/e2e/statelang/*.json]`
+
+## Run individual scenarios
+
+- Start + run (fresh): `saga/e2e/up_and_run.sh`
+- Success only: `saga/e2e/run.sh [seatago.yaml] [config.yaml]`
+- Compensation: `saga/e2e/run_compensation.sh [seatago.yaml] [config.yaml] 
[compensate-balance|compensate-inventory]`
+
+## DB validation
+
+```
+go run ./saga/e2e/dbcheck \
+  -engine saga/e2e/config.yaml \
+  -xid <XID> \
+  -scenario success|compensate-balance|compensate-inventory
+```
+
+Checks machine row end state and per‑state rows with compensation linkage.
+
+## Schema migration
+
+```
+MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 \
+MYSQL_USER=root MYSQL_PWD=secret MYSQL_DB=seata_saga \
+saga/e2e/migrate.sh
+```
+
+## Troubleshooting
+
+- Seata unreachable → check `service.grouplist.default`
+- Branch register fails → ensure `application-id#tx-service-group` mapping 
exists in TC
+- MySQL initial EOF/bad connection → increase `WAIT_READY_MARGIN`
+- Optimistic‑lock 0‑row finish on success → benign; handled idempotently 
(debug only)
+- TC GlobalReport timeouts → logged, do not fail local finish; DB validation 
still passes
+
+## Pointers
+
+- StateLang JSON: `statelang/reduce_inventory_and_balance.json`
+- Engine: `pkg/saga/statemachine/engine/pcext/*`, store: 
`pkg/saga/statemachine/store/db/statelog.go`
+- Scripts: `run_all.sh`, `up_and_run.sh`, `run.sh`, `run_compensation.sh`
diff --git a/saga/e2e/README_zh.md b/saga/e2e/README_zh.md
new file mode 100644
index 0000000..d93687b
--- /dev/null
+++ b/saga/e2e/README_zh.md
@@ -0,0 +1,134 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+# Saga E2E(MySQL)— 端到端使用说明
+
+本示例在 `saga/e2e` 目录下,提供一键脚本与 DB 校验工具。
+
+## 一键运行(推荐)
+
+前置条件:本机需安装 Go 1.20+、Docker 与 docker-compose。
+
+使用 docker‑compose 重建/启动 MySQL 与 Seata Server,等待就绪后依次运行 3 个场景并进行 DB 校验:
+
+```
+saga/e2e/run_all.sh --up \
+  --seata saga/e2e/seatago.yaml \
+  --engine saga/e2e/config.yaml
+```
+
+## 使用本地 seata-go 进行调试
+
+sample 仓库当前依赖发布版的 seata-go。如需在调试时直接复用
+`../incubator-seata-go` 的最新代码,可在运行前临时添加 replace:
+
+```
+go mod edit -replace seata.apache.org/seata-go=../incubator-seata-go
+go mod tidy # 依赖有变更时执行
+```
+
+执行完成后建议移除 replace,保持 go.mod 干净:
+
+```
+go mod edit -dropreplace seata.apache.org/seata-go
+```
+
+如果使用 Go 1.18+,也可以临时创建 workspace:
+
+```
+go work init
+go work use .
+go work use ../incubator-seata-go
+go run ./saga/e2e
+rm go.work
+```
+
+说明:
+- 脚本会 `docker-compose down -v` 清理旧容器与卷,再 `up -d --force-recreate` 全新拉起
+- 自动等待 MySQL、Seata Server 端口就绪,并额外等待一段时间,避免刚启动时 MySQL 的 EOF 抖动
+- 依次运行 3 个场景并进行 DB 校验:
+  - success:前向执行成功
+  - compensate-balance:余额扣减失败触发补偿,最终 Fail(补偿 SU)
+  - compensate-inventory:库存首步失败,无补偿,最终 Fail
+
+可调参数(环境变量):
+- `WAIT_TIMEOUT`(默认 60):端口等待超时秒数
+- `WAIT_INTERVAL`(默认 2):端口轮询间隔秒数
+- `WAIT_READY_MARGIN`(默认 15):端口可达后的额外等待秒数
+
+预期输出:
+- `DB validation (success) OK`
+- `DB validation (compensate-balance) OK`
+- `DB validation (compensate-inventory) OK`
+- `[+] All e2e scenarios finished`
+
+## 配置
+
+- Seata 客户端(`seatago.yaml`)
+  - `application-id`、`tx-service-group`
+  - `service.grouplist.default`:Seata Server 地址(`ip:port`)
+
+- Saga 引擎(`config.yaml`)
+  - `store_enabled: true`、`store_type: mysql`
+  - `store_dsn: user:pass@tcp(127.0.0.1:3306)/seata_saga?parseTime=true`
+  - `tc_enabled: true`
+  - `state_machine_resources: [saga/e2e/statelang/*.json]`
+
+## 单场景运行
+
+- 启动并运行(重建容器):`saga/e2e/up_and_run.sh`
+- 仅运行成功场景:`saga/e2e/run.sh [seatago.yaml] [config.yaml]`
+- 运行补偿场景:`saga/e2e/run_compensation.sh [seatago.yaml] [config.yaml] 
[compensate-balance|compensate-inventory]`
+
+## DB 校验工具
+
+用于在运行结束后对最终状态进行校验:
+
+```
+go run ./saga/e2e/dbcheck \
+  -engine saga/e2e/config.yaml \
+  -xid <XID> \
+  -scenario success|compensate-balance|compensate-inventory
+```
+
+校验点包括:
+- `seata_state_machine_inst`:最终 
`status`、`compensation_status`、`is_running`、`gmt_end`
+- `seata_state_inst`:关键前向/补偿状态与 `state_id_compensated_for` 关联
+
+## 初始化数据库表(非 docker‑compose 场景)
+
+```
+MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 \
+MYSQL_USER=root MYSQL_PWD=secret MYSQL_DB=seata_saga \
+saga/e2e/migrate.sh
+```
+
+以上脚本会执行 `sql/mysql_saga_schema.sql` 创建相关表。
+
+## 常见问题
+
+- 无法连接 Seata Server:检查 `seatago.yaml` 的 `service.grouplist.default` 与网络连通性。
+- BranchRegister 失败或 branchId 非法:确认资源标识 `applicationId#txServiceGroup` 与 TC 的 
vgroup 映射一致。
+- MySQL 刚启动时 EOF/“bad connection”:增大 `WAIT_READY_MARGIN`(如 20)。
+- 成功场景“乐观锁 0 行更新”提示:幂等/并发下的正常现象,已降为 debug,不影响最终结果。
+- TC GlobalReport 超时:仅记录日志,不影响本地最终态;DB 校验仍应通过(与 Java 行为一致)。
+
+## 参考路径
+
+- 状态机 JSON:`statelang/reduce_inventory_and_balance.json`
+- 
引擎/持久化关键路径:`pkg/saga/statemachine/engine/pcext/*`、`pkg/saga/statemachine/store/db/statelog.go`
+- 脚本:`run_all.sh`、`up_and_run.sh`、`run.sh`、`run_compensation.sh`
diff --git a/goimports.sh b/saga/e2e/config.yaml
similarity index 61%
copy from goimports.sh
copy to saga/e2e/config.yaml
index 4c30f1c..0261527 100644
--- a/goimports.sh
+++ b/saga/e2e/config.yaml
@@ -1,4 +1,3 @@
-#
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
 # this work for additional information regarding copyright ownership.
@@ -13,17 +12,22 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-# format go imports style
-go install -v golang.org/x/tools/cmd/goimports
-goimports  -w .
+# Engine runtime config (loaded by DefaultStateMachineConfig)
+
+trans_operation_timeout: 60000
+service_invoke_timeout: 5000
+rm_report_success_enable: true
+saga_branch_register_enable: true
+
+# Register JSON resources (update path if you move files)
+state_machine_resources:
+  - saga/e2e/statelang/*.json
 
-# format licence style
-go install github.com/apache/skywalking-eyes/cmd/license-eye@latest
-license-eye header fix
-# check dependency licence is valid
-license-eye dependency check
+# Enable MySQL store (fill in your DSN)
+store_enabled: true
+store_type: mysql
+store_dsn: "root:secret@tcp(127.0.0.1:3306)/seata_saga?parseTime=true"
 
-# format go.mod
-go mod tidy
+# Enable TC integration
+tc_enabled: true
diff --git a/saga/e2e/dbcheck/main.go b/saga/e2e/dbcheck/main.go
new file mode 100644
index 0000000..21a0f17
--- /dev/null
+++ b/saga/e2e/dbcheck/main.go
@@ -0,0 +1,329 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+       "database/sql"
+       "errors"
+       "flag"
+       "fmt"
+       "os"
+       "sort"
+       "strings"
+
+       _ "github.com/go-sql-driver/mysql"
+       "gopkg.in/yaml.v3"
+)
+
+type engineConf struct {
+       StoreEnabled bool   `yaml:"store_enabled"`
+       StoreType    string `yaml:"store_type"`
+       StoreDSN     string `yaml:"store_dsn"`
+}
+
+func loadEngineConf(path string) (*engineConf, error) {
+       b, err := os.ReadFile(path)
+       if err != nil {
+               return nil, err
+       }
+       var c engineConf
+       if err := yaml.Unmarshal(b, &c); err != nil {
+               return nil, err
+       }
+       if !c.StoreEnabled || c.StoreType == "" || c.StoreDSN == "" {
+               return nil, errors.New("engineConf missing 
store_enabled/store_type/store_dsn")
+       }
+       return &c, nil
+}
+
+func expectSuccess(row smRow) error {
+       var errs []string
+       if row.Status != "SU" {
+               errs = append(errs, fmt.Sprintf("status=%s want=SU", 
row.Status))
+       }
+       if row.CompStatus.Valid && row.CompStatus.String != "" {
+               errs = append(errs, fmt.Sprintf("compensation_status=%s 
want=''", row.CompStatus.String))
+       }
+       if row.IsRunning != 0 {
+               errs = append(errs, fmt.Sprintf("is_running=%d want=0", 
row.IsRunning))
+       }
+       if !row.GmtEnd.Valid {
+               errs = append(errs, "gmt_end is NULL")
+       }
+       if row.Excep.Valid && len(row.Excep.String) > 0 {
+               errs = append(errs, "excep not empty")
+       }
+       if len(errs) > 0 {
+               return errors.New(strings.Join(errs, "; "))
+       }
+       return nil
+}
+
+func expectCompensateFail(row smRow) error {
+       var errs []string
+       if row.Status != "FA" {
+               errs = append(errs, fmt.Sprintf("status=%s want=FA", 
row.Status))
+       }
+       if !row.CompStatus.Valid || row.CompStatus.String != "SU" {
+               want := "<NULL>"
+               if row.CompStatus.Valid {
+                       want = row.CompStatus.String
+               }
+               errs = append(errs, fmt.Sprintf("compensation_status=%s 
want=SU", want))
+       }
+       if row.IsRunning != 0 {
+               errs = append(errs, fmt.Sprintf("is_running=%d want=0", 
row.IsRunning))
+       }
+       if !row.GmtEnd.Valid {
+               errs = append(errs, "gmt_end is NULL")
+       }
+       // excep may be empty for Fail end state; don't enforce
+       if len(errs) > 0 {
+               return errors.New(strings.Join(errs, "; "))
+       }
+       return nil
+}
+
+type smRow struct {
+       ID         string
+       Status     string
+       CompStatus sql.NullString
+       IsRunning  int
+       GmtEnd     sql.NullTime
+       Excep      sql.NullString
+}
+
+type stRow struct {
+       Name    string
+       Type    string
+       Status  string
+       CompFor sql.NullString
+}
+
+func fetchStateRows(db *sql.DB, xid string) ([]stRow, error) {
+       q := `SELECT name, type, status, state_id_compensated_for 
+          FROM seata_state_inst WHERE machine_inst_id=? ORDER BY gmt_started 
ASC`
+       rows, err := db.Query(q, xid)
+       if err != nil {
+               return nil, err
+       }
+       defer rows.Close()
+       var out []stRow
+       for rows.Next() {
+               var r stRow
+               if err := rows.Scan(&r.Name, &r.Type, &r.Status, &r.CompFor); 
err != nil {
+                       return nil, err
+               }
+               out = append(out, r)
+       }
+       return out, rows.Err()
+}
+
+func contains(rows []stRow, pred func(stRow) bool) bool {
+       for _, r := range rows {
+               if pred(r) {
+                       return true
+               }
+       }
+       return false
+}
+
+func validateStatesSuccess(rows []stRow) error {
+       var errs []string
+       if !contains(rows, func(r stRow) bool {
+               return r.Name == "ReduceInventory" && r.Status == "SU" && 
!(r.CompFor.Valid && r.CompFor.String != "")
+       }) {
+               errs = append(errs, "missing SU ReduceInventory forward state")
+       }
+       if !contains(rows, func(r stRow) bool {
+               return r.Name == "ReduceBalance" && r.Status == "SU" && 
!(r.CompFor.Valid && r.CompFor.String != "")
+       }) {
+               errs = append(errs, "missing SU ReduceBalance forward state")
+       }
+       // no compensation states expected
+       if contains(rows, func(r stRow) bool { return r.CompFor.Valid && 
r.CompFor.String != "" }) {
+               errs = append(errs, "unexpected compensation state present")
+       }
+       // Succeed end state is not persisted as state_inst in Go impl; rely on 
machine_inst status instead
+       if len(errs) > 0 {
+               return errors.New(strings.Join(errs, "; "))
+       }
+       return nil
+}
+
+func validateStatesCompBalance(rows []stRow) error {
+       var errs []string
+       if !contains(rows, func(r stRow) bool {
+               return r.Name == "ReduceInventory" && r.Status == "SU" && 
!(r.CompFor.Valid && r.CompFor.String != "")
+       }) {
+               errs = append(errs, "missing SU ReduceInventory forward state")
+       }
+       if !contains(rows, func(r stRow) bool {
+               return r.Name == "ReduceBalance" && r.Status == "FA" && 
!(r.CompFor.Valid && r.CompFor.String != "")
+       }) {
+               errs = append(errs, "missing FA ReduceBalance forward state")
+       }
+       if !contains(rows, func(r stRow) bool {
+               return r.Name == "CompensateReduceInventory" && r.Status == 
"SU" && r.CompFor.Valid && r.CompFor.String != ""
+       }) {
+               // Allow type-based match in case of name differences
+               if !contains(rows, func(r stRow) bool {
+                       return strings.HasPrefix(r.Name, "Compensate") && 
r.Status == "SU" && r.CompFor.Valid && r.CompFor.String != ""
+               }) {
+                       errs = append(errs, "missing SU compensation state for 
ReduceInventory")
+               }
+       }
+       // Fail end state is not persisted as state_inst in Go impl; rely on 
machine_inst status instead
+       if len(errs) > 0 {
+               return errors.New(strings.Join(errs, "; "))
+       }
+       return nil
+}
+
+func validateStatesCompInventory(rows []stRow) error {
+       var errs []string
+       if !contains(rows, func(r stRow) bool {
+               return r.Name == "ReduceInventory" && r.Status == "FA" && 
!(r.CompFor.Valid && r.CompFor.String != "")
+       }) {
+               errs = append(errs, "missing FA ReduceInventory forward state")
+       }
+       // no compensation states expected
+       if contains(rows, func(r stRow) bool { return r.CompFor.Valid && 
r.CompFor.String != "" }) {
+               // If any compensation exists, include a snapshot for debugging
+               var names []string
+               for _, r := range rows {
+                       if r.CompFor.Valid && r.CompFor.String != "" {
+                               names = append(names, r.Name)
+                       }
+               }
+               sort.Strings(names)
+               errs = append(errs, fmt.Sprintf("unexpected compensation states 
present: %s", strings.Join(names, ",")))
+       }
+       // Fail end state row not required; machine status covers it
+       if len(errs) > 0 {
+               return errors.New(strings.Join(errs, "; "))
+       }
+       return nil
+}
+
+func main() {
+       var engineConfPath, xid, scenario string
+       flag.StringVar(&engineConfPath, "engine", "", "engine config path")
+       flag.StringVar(&xid, "xid", "", "XID to validate")
+       flag.StringVar(&scenario, "scenario", "success", 
"success|compensate-balance|compensate-inventory")
+       flag.Parse()
+       if engineConfPath == "" || xid == "" {
+               fmt.Fprintln(os.Stderr, "missing --engine or --xid")
+               os.Exit(2)
+       }
+
+       cfg, err := loadEngineConf(engineConfPath)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(2)
+       }
+       if cfg.StoreType != "mysql" {
+               fmt.Fprintln(os.Stderr, "only mysql is supported in dbcheck")
+               os.Exit(2)
+       }
+       db, err := sql.Open("mysql", cfg.StoreDSN)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(2)
+       }
+       defer db.Close()
+       if err := db.Ping(); err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(2)
+       }
+
+       // query state_machine_inst row
+       q := `SELECT id, status, compensation_status, is_running, gmt_end, 
excep FROM seata_state_machine_inst WHERE id=?`
+       var row smRow
+       if err := db.QueryRow(q, xid).Scan(&row.ID, &row.Status, 
&row.CompStatus, &row.IsRunning, &row.GmtEnd, &row.Excep); err != nil {
+               fmt.Fprintf(os.Stderr, "query sm row failed: %v\n", err)
+               os.Exit(2)
+       }
+       states, err := fetchStateRows(db, xid)
+       if err != nil {
+               fmt.Fprintf(os.Stderr, "query state rows failed: %v\n", err)
+               os.Exit(2)
+       }
+       // Debug visibility: show how many rows we saw for this XID
+       fmt.Printf("Found %d state rows for XID=%s\n", len(states), xid)
+       if len(states) > 0 {
+               var names []string
+               for _, r := range states {
+                       names = append(names, r.Name+"/"+r.Status)
+               }
+               fmt.Printf("States: %s\n", strings.Join(names, ", "))
+       }
+
+       switch scenario {
+       case "success":
+               if err := expectSuccess(row); err != nil {
+                       fmt.Fprintf(os.Stderr, "validation failed (machine): 
%v\n", err)
+                       os.Exit(1)
+               }
+               if err := validateStatesSuccess(states); err != nil {
+                       fmt.Fprintf(os.Stderr, "validation failed (states): 
%v\n", err)
+                       os.Exit(1)
+               }
+               fmt.Println("DB validation (success) OK")
+               return
+       case "compensate-balance":
+               if err := expectCompensateFail(row); err != nil {
+                       fmt.Fprintf(os.Stderr, "validation failed (machine): 
%v\n", err)
+                       os.Exit(1)
+               }
+               if err := validateStatesCompBalance(states); err != nil {
+                       fmt.Fprintf(os.Stderr, "validation failed (states): 
%v\n", err)
+                       os.Exit(1)
+               }
+               fmt.Println("DB validation (compensate-balance) OK")
+               return
+       case "compensate-inventory":
+               // machine expectation: forward fail without compensation
+               var errs []string
+               if row.Status != "FA" {
+                       errs = append(errs, fmt.Sprintf("status=%s want=FA", 
row.Status))
+               }
+               if row.CompStatus.Valid && row.CompStatus.String != "" {
+                       errs = append(errs, fmt.Sprintf("compensation_status=%s 
want=''", row.CompStatus.String))
+               }
+               if row.IsRunning != 0 {
+                       errs = append(errs, fmt.Sprintf("is_running=%d want=0", 
row.IsRunning))
+               }
+               if !row.GmtEnd.Valid {
+                       errs = append(errs, "gmt_end is NULL")
+               }
+               if len(errs) > 0 {
+                       fmt.Fprintf(os.Stderr, "validation failed (machine): 
%s\n", strings.Join(errs, "; "))
+                       os.Exit(1)
+               }
+               if err := validateStatesCompInventory(states); err != nil {
+                       fmt.Fprintf(os.Stderr, "validation failed (states): 
%v\n", err)
+                       os.Exit(1)
+               }
+               fmt.Println("DB validation (compensate-inventory) OK")
+               return
+       default:
+               fmt.Fprintf(os.Stderr, "unknown scenario: %s\n", scenario)
+               os.Exit(2)
+       }
+}
diff --git a/goimports.sh b/saga/e2e/docker-compose.yml
similarity index 58%
copy from goimports.sh
copy to saga/e2e/docker-compose.yml
index 4c30f1c..944e79e 100644
--- a/goimports.sh
+++ b/saga/e2e/docker-compose.yml
@@ -1,4 +1,3 @@
-#
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
 # this work for additional information regarding copyright ownership.
@@ -13,17 +12,28 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
-
-# format go imports style
-go install -v golang.org/x/tools/cmd/goimports
-goimports  -w .
 
-# format licence style
-go install github.com/apache/skywalking-eyes/cmd/license-eye@latest
-license-eye header fix
-# check dependency licence is valid
-license-eye dependency check
+services:
+  mysql:
+    image: mysql:8.0
+    container_name: saga_mysql
+    restart: unless-stopped
+    environment:
+      MYSQL_ROOT_PASSWORD: secret
+      MYSQL_DATABASE: seata_saga
+    ports:
+      - "3306:3306"
+    volumes:
+      - ./sql/mysql_saga_schema.sql:/docker-entrypoint-initdb.d/1_saga.sql:ro
 
-# format go.mod
-go mod tidy
+  seata-server:
+    image: seataio/seata-server:1.6.1
+    container_name: seata_server
+    restart: unless-stopped
+    environment:
+      - SEATA_IP=0.0.0.0
+      - SEATA_PORT=8091
+    ports:
+      - "8091:8091"
+    depends_on:
+      - mysql
diff --git a/saga/e2e/main.go b/saga/e2e/main.go
new file mode 100644
index 0000000..a29645f
--- /dev/null
+++ b/saga/e2e/main.go
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+       "context"
+       "database/sql"
+       "flag"
+       "fmt"
+       "net"
+       "os"
+       "path/filepath"
+       "strings"
+       "time"
+
+       _ "github.com/go-sql-driver/mysql"
+       "gopkg.in/yaml.v3"
+
+       "seata.apache.org/seata-go/pkg/client"
+       engcfg "seata.apache.org/seata-go/pkg/saga/statemachine/engine/config"
+       "seata.apache.org/seata-go/pkg/saga/statemachine/engine/core"
+       "seata.apache.org/seata-go/pkg/saga/statemachine/engine/invoker"
+)
+
+// InventoryAction (DB-backed) implements reduce/compensate with explicit 
params
+type InventoryAction struct{ db *sql.DB }
+
+func NewInventoryAction(db *sql.DB) *InventoryAction { return 
&InventoryAction{db: db} }
+
+// Reduce(businessKey, productId, count) -> (bool, error)
+func (a *InventoryAction) Reduce(businessKey string, productId string, count 
int) (bool, error) {
+       if count <= 0 {
+               count = 1
+       }
+       res, err := a.db.Exec("UPDATE e2e_inventory SET stock = stock - ? WHERE 
product_id = ? AND stock >= ?", count, productId, count)
+       if err != nil {
+               return false, err
+       }
+       if n, _ := res.RowsAffected(); n == 0 {
+               return false, fmt.Errorf("INVENTORY_NOT_ENOUGH")
+       }
+       fmt.Printf("InventoryAction.Reduce: biz=%s, product=%s, count=%d\n", 
businessKey, productId, count)
+       return true, nil
+}
+
+func (a *InventoryAction) CompensateReduce(businessKey string, productId 
string, count int) (bool, error) {
+       if count <= 0 {
+               count = 1
+       }
+       if _, err := a.db.Exec("UPDATE e2e_inventory SET stock = stock + ? 
WHERE product_id = ?", count, productId); err != nil {
+               return false, err
+       }
+       fmt.Printf("InventoryAction.CompensateReduce: biz=%s, product=%s, 
count=%d\n", businessKey, productId, count)
+       return true, nil
+}
+
+// BalanceAction (DB-backed) implements reduce/compensate with explicit params
+type BalanceAction struct{ db *sql.DB }
+
+func NewBalanceAction(db *sql.DB) *BalanceAction { return &BalanceAction{db: 
db} }
+
+// Reduce(businessKey, userId, amount) -> (bool, error)
+func (b *BalanceAction) Reduce(businessKey string, userId string, amount int) 
(bool, error) {
+       if amount <= 0 {
+               amount = 1
+       }
+       res, err := b.db.Exec("UPDATE e2e_balance SET amount = amount - ? WHERE 
user_id = ? AND amount >= ?", amount, userId, amount)
+       if err != nil {
+               return false, err
+       }
+       if n, _ := res.RowsAffected(); n == 0 {
+               return false, fmt.Errorf("BALANCE_NOT_ENOUGH")
+       }
+       fmt.Printf("BalanceAction.Reduce: biz=%s, user=%s, amount=%d\n", 
businessKey, userId, amount)
+       return true, nil
+}
+
+func (b *BalanceAction) CompensateReduce(businessKey string, userId string, 
amount int) (bool, error) {
+       if amount <= 0 {
+               amount = 1
+       }
+       if _, err := b.db.Exec("UPDATE e2e_balance SET amount = amount + ? 
WHERE user_id = ?", amount, userId); err != nil {
+               return false, err
+       }
+       fmt.Printf("BalanceAction.CompensateReduce: biz=%s, user=%s, 
amount=%d\n", businessKey, userId, amount)
+       return true, nil
+}
+
+func main() {
+       var seataConf string
+       var engineConf string
+       flag.StringVar(&seataConf, "seataConf", "saga/e2e/seatago.yaml", "path 
to seata-go client yaml")
+       flag.StringVar(&engineConf, "engineConf", "saga/e2e/config.yaml", "path 
to saga engine config")
+       flag.Parse()
+
+       client.InitPath(seataConf)
+       if err := checkSeataConnectivity(seataConf); err != nil {
+               fmt.Fprintf(os.Stderr, "Seata server connectivity check failed: 
%v\n", err)
+               os.Exit(1)
+       }
+
+       eng, err := core.NewProcessCtrlStateMachineEngine()
+       if err != nil {
+               fmt.Fprintf(os.Stderr, "create state machine engine failed: 
%v\n", err)
+               os.Exit(1)
+       }
+       cfgIface := eng.GetStateMachineConfig()
+       if cfg, ok := cfgIface.(*engcfg.DefaultStateMachineConfig); ok {
+               if err := cfg.LoadConfig(engineConf); err != nil {
+                       fmt.Fprintf(os.Stderr, "load engine config failed: 
%v\n", err)
+                       os.Exit(1)
+               }
+               if wd, err := os.Getwd(); err == nil {
+                       absPattern := filepath.Join(wd, 
"saga/e2e/statelang/*.json")
+                       _ = cfg.RegisterStateMachineDef([]string{absPattern})
+               }
+               if err := cfg.Init(); err != nil {
+                       fmt.Fprintf(os.Stderr, "init engine config failed: 
%v\n", err)
+                       os.Exit(1)
+               }
+       } else {
+               fmt.Fprintf(os.Stderr, "unexpected state machine config type: 
%T\n", cfgIface)
+               os.Exit(1)
+       }
+
+       // Open business DB and seed data
+       bizDB, err := openBusinessDB(engineConf)
+       if err != nil {
+               fmt.Fprintf(os.Stderr, "open business db failed: %v\n", err)
+               os.Exit(1)
+       }
+       if err := seedBusinessData(bizDB); err != nil {
+               fmt.Fprintf(os.Stderr, "seed business data failed: %v\n", err)
+               os.Exit(1)
+       }
+
+       // Register local services (DB-backed)
+       if lv := cfgIface.ServiceInvokerManager().ServiceInvoker("local"); lv 
!= nil {
+               if lsi, ok := lv.(*invoker.LocalServiceInvoker); ok {
+                       lsi.RegisterService("inventoryAction", 
NewInventoryAction(bizDB))
+                       lsi.RegisterService("balanceAction", 
NewBalanceAction(bizDB))
+               }
+       }
+
+       // Run three scenarios sequentially, driven by parameters (no orders 
table)
+       scenarios := []struct {
+               name   string
+               params map[string]any
+       }{
+               {"success", successParams()},
+               {"compensate-balance", compensateBalanceParams()},
+               {"compensate-inventory", compensateInventoryParams()},
+       }
+       for _, sc := range scenarios {
+               inst, err := eng.Start(context.Background(), 
"ReduceInventoryAndBalance", "", sc.params)
+               if err != nil {
+                       fmt.Fprintf(os.Stderr, "start saga failed (%s): %v\n", 
sc.name, err)
+                       os.Exit(1)
+               }
+               
fmt.Println("======================================================")
+               fmt.Printf("SCENARIO %s XID=%s status=%s compStatus=%s\n", 
sc.name, inst.ID(), inst.Status(), inst.CompensationStatus())
+               
fmt.Println("======================================================")
+
+               fmt.Println("")
+               fmt.Println("")
+               fmt.Println("")
+               time.Sleep(time.Second * 2)
+       }
+}
+
+type runtimeStoreConf struct {
+       StoreEnabled bool   `yaml:"store_enabled"`
+       StoreType    string `yaml:"store_type"`
+       StoreDSN     string `yaml:"store_dsn"`
+       TCEnabled    bool   `yaml:"tc_enabled"`
+}
+
+// checkSeataConnectivity parses grouplist from seatago.yaml and dials the 
first address
+func checkSeataConnectivity(seataConf string) error {
+       raw, err := os.ReadFile(seataConf)
+       if err != nil {
+               return err
+       }
+       type seataYaml struct {
+               Seata struct {
+                       Service struct {
+                               GroupList map[string]string `yaml:"grouplist"`
+                       } `yaml:"service"`
+               } `yaml:"seata"`
+       }
+       var cfg seataYaml
+       if err := yaml.Unmarshal(raw, &cfg); err != nil {
+               return err
+       }
+       for _, addr := range cfg.Seata.Service.GroupList {
+               target := addr
+               // allow multiple via ","
+               if strings.Contains(addr, ",") {
+                       parts := strings.Split(addr, ",")
+                       target = strings.TrimSpace(parts[0])
+               }
+               if target == "" {
+                       continue
+               }
+               conn, err := net.DialTimeout("tcp", target, 2_000_000_000)
+               if err != nil {
+                       return fmt.Errorf("dial %s failed: %w", target, err)
+               }
+               _ = conn.Close()
+               return nil
+       }
+       return fmt.Errorf("no seata service.grouplist address found in %s", 
seataConf)
+}
+
+// openBusinessDB opens a DB connection using engine store_dsn from YAML.
+func openBusinessDB(engineConf string) (*sql.DB, error) {
+       raw, err := os.ReadFile(engineConf)
+       if err != nil {
+               return nil, err
+       }
+       var r runtimeStoreConf
+       if err := yaml.Unmarshal(raw, &r); err != nil {
+               return nil, err
+       }
+       if !r.StoreEnabled || r.StoreType == "" || r.StoreDSN == "" {
+               return nil, fmt.Errorf("engine store not configured")
+       }
+       driver := r.StoreType
+       if driver == "sqlite" || driver == "sqlite3" {
+               driver = "sqlite3"
+       }
+       db, err := sql.Open(driver, r.StoreDSN)
+       if err != nil {
+               return nil, err
+       }
+       if err := db.Ping(); err != nil {
+               db.Close()
+               return nil, err
+       }
+       return db, nil
+}
+
+// seedBusinessData creates business tables and deterministic rows to drive 
three scenarios
+func seedBusinessData(db *sql.DB) error {
+       // tables
+       _, err := db.Exec(`CREATE TABLE IF NOT EXISTS e2e_inventory (
+  product_id VARCHAR(64) PRIMARY KEY,
+  stock INT NOT NULL
+)`)
+       if err != nil {
+               return err
+       }
+       _, err = db.Exec(`CREATE TABLE IF NOT EXISTS e2e_balance (
+  user_id VARCHAR(64) PRIMARY KEY,
+  amount INT NOT NULL
+)`)
+       if err != nil {
+               return err
+       }
+       // upserts (MySQL syntax)
+       // inventories
+       if _, err := db.Exec(`INSERT INTO e2e_inventory(product_id, stock) 
VALUES
+        ('p_s', 100), ('p_b', 100), ('p_i', 100)
+      ON DUPLICATE KEY UPDATE stock=VALUES(stock)`); err != nil {
+               return err
+       }
+       // balances
+       if _, err := db.Exec(`INSERT INTO e2e_balance(user_id, amount) VALUES
+        ('u_s', 1000), ('u_b', 50), ('u_i', 1000)
+      ON DUPLICATE KEY UPDATE amount=VALUES(amount)`); err != nil {
+               return err
+       }
+       return nil
+}
+
+// no time helpers needed
diff --git a/goimports.sh b/saga/e2e/migrate.sh
old mode 100644
new mode 100755
similarity index 62%
copy from goimports.sh
copy to saga/e2e/migrate.sh
index 4c30f1c..793f902
--- a/goimports.sh
+++ b/saga/e2e/migrate.sh
@@ -1,4 +1,4 @@
-#
+#!/usr/bin/env bash
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
 # this work for additional information regarding copyright ownership.
@@ -13,17 +13,14 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-# format go imports style
-go install -v golang.org/x/tools/cmd/goimports
-goimports  -w .
+set -euo pipefail
 
-# format licence style
-go install github.com/apache/skywalking-eyes/cmd/license-eye@latest
-license-eye header fix
-# check dependency licence is valid
-license-eye dependency check
+if [ -z "${MYSQL_HOST:-}" ] || [ -z "${MYSQL_PORT:-}" ] || [ -z 
"${MYSQL_USER:-}" ] || [ -z "${MYSQL_PWD:-}" ] || [ -z "${MYSQL_DB:-}" ]; then
+  echo "Please set MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PWD, MYSQL_DB"
+  exit 1
+fi
 
-# format go.mod
-go mod tidy
+echo "Applying schema to $MYSQL_HOST:$MYSQL_PORT/$MYSQL_DB ..."
+mysql -h"$MYSQL_HOST" -P"$MYSQL_PORT" -u"$MYSQL_USER" -p"$MYSQL_PWD" 
"$MYSQL_DB" < saga/e2e/sql/mysql_saga_schema.sql
+echo "Schema applied."
diff --git a/goimports.sh b/saga/e2e/run.sh
old mode 100644
new mode 100755
similarity index 72%
copy from goimports.sh
copy to saga/e2e/run.sh
index 4c30f1c..ea2b43f
--- a/goimports.sh
+++ b/saga/e2e/run.sh
@@ -1,4 +1,4 @@
-#
+#!/usr/bin/env bash
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
 # this work for additional information regarding copyright ownership.
@@ -13,17 +13,11 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-# format go imports style
-go install -v golang.org/x/tools/cmd/goimports
-goimports  -w .
+set -euo pipefail
 
-# format licence style
-go install github.com/apache/skywalking-eyes/cmd/license-eye@latest
-license-eye header fix
-# check dependency licence is valid
-license-eye dependency check
+SEATA_CONF=${1:-saga/e2e/seatago.yaml}
+ENGINE_CONF=${2:-saga/e2e/config.yaml}
 
-# format go.mod
-go mod tidy
+echo "Running saga e2e with seataConf=$SEATA_CONF engineConf=$ENGINE_CONF"
+go run ./saga/e2e -seataConf="$SEATA_CONF" -engineConf="$ENGINE_CONF"
diff --git a/saga/e2e/run_all.sh b/saga/e2e/run_all.sh
new file mode 100755
index 0000000..6933e86
--- /dev/null
+++ b/saga/e2e/run_all.sh
@@ -0,0 +1,144 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+# One-click e2e: optional docker-compose up + readiness wait, then run all 
scenarios.
+
+DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
+REPO_ROOT="$DIR/../.."
+
+SEATA_CONF=${SEATA_CONF:-"$DIR/seatago.yaml"}
+ENGINE_CONF=${ENGINE_CONF:-"$DIR/config.yaml"}
+DO_UP=${DO_UP:-"false"}
+# Wait settings (seconds)
+WAIT_TIMEOUT=${WAIT_TIMEOUT:-60}
+WAIT_INTERVAL=${WAIT_INTERVAL:-2}
+# Extra margin after MySQL becomes reachable (to avoid initial EOF on fresh 
start)
+WAIT_READY_MARGIN=${WAIT_READY_MARGIN:-15}
+
+usage() {
+  cat <<EOF
+Usage: $(basename "$0") [--up] [--seata <seatago.yaml>] [--engine 
<config.yaml>]
+
+Options:
+  --up                 Start docker-compose (MySQL + Seata Server) before 
running
+  --seata <file>       Path to seatago.yaml (default: $SEATA_CONF)
+  --engine <file>      Path to engine config (default: $ENGINE_CONF)
+
+Runs three scenarios in order:
+  1) success
+  2) compensate-balance
+  3) compensate-inventory
+EOF
+}
+
+while [[ $# -gt 0 ]]; do
+  case "$1" in
+    --up) DO_UP="true"; shift ;;
+    --seata) SEATA_CONF="$2"; shift 2 ;;
+    --engine) ENGINE_CONF="$2"; shift 2 ;;
+    -h|--help) usage; exit 0 ;;
+    *) echo "Unknown arg: $1"; usage; exit 1 ;;
+  esac
+done
+
+require() { command -v "$1" >/dev/null 2>&1 || { echo "Missing required 
command: $1"; exit 1; }; }
+
+echo "[+] Repo root: $REPO_ROOT"
+cd "$REPO_ROOT"
+
+require go
+
+[[ -f "$SEATA_CONF" ]] || { echo "seatago.yaml not found: $SEATA_CONF"; exit 
1; }
+[[ -f "$ENGINE_CONF" ]] || { echo "engine config not found: $ENGINE_CONF"; 
exit 1; }
+
+if [[ "$DO_UP" == "true" ]]; then
+  require docker-compose || require docker
+  echo "[+] Resetting docker-compose services (MySQL + Seata Server) ..."
+  # Stop and remove old containers, networks and volumes to ensure a clean 
start
+  docker-compose -f "$DIR/docker-compose.yml" down -v --remove-orphans || true
+  docker-compose -f "$DIR/docker-compose.yml" rm -f -s -v || true
+  echo "[+] Starting docker-compose services (fresh) ..."
+  docker-compose -f "$DIR/docker-compose.yml" up -d --force-recreate
+fi
+
+wait_for_tcp() {
+  local host="$1"; local port="$2"; local name="$3"; local 
timeout="${4:-$WAIT_TIMEOUT}"; local interval="${5:-$WAIT_INTERVAL}"
+  echo "[+] Waiting for $name at $host:$port (timeout=${timeout}s) ..."
+  local start_ts=$(date +%s)
+  while true; do
+    if command -v nc >/dev/null 2>&1; then
+      if nc -z "$host" "$port" 2>/dev/null; then echo "[+] $name is 
reachable"; return 0; fi
+    else
+      (exec 3<>/dev/tcp/$host/$port) 2>/dev/null && { exec 3>&- || true; echo 
"[+] $name is reachable"; return 0; }
+    fi
+    local now=$(date +%s)
+    if (( now - start_ts >= timeout )); then
+      echo "[-] Timeout waiting for $name at $host:$port"; return 1
+    fi
+    sleep "$interval"
+  done
+}
+
+# Parse Seata target from seatago.yaml
+SEATA_ADDR=$(awk '
+  /grouplist:/ { gl=1; next }
+  gl==1 && /^[^[:space:]]/ { gl=0 }
+  gl==1 && /default:/ {
+    line=$0; gsub(/"/,"",line); sub(/.*default:[[:space:]]*/,"",line); print 
line; exit;
+  }
+' "$SEATA_CONF" 2>/dev/null || true)
+[[ -n "$SEATA_ADDR" ]] || { echo "[-] Could not extract Seata 
service.grouplist.default from $SEATA_CONF"; exit 1; }
+SEATA_HOST=${SEATA_ADDR%%:*}
+SEATA_PORT=${SEATA_ADDR##*:}
+
+# Parse MySQL from engine store_dsn (root:pwd@tcp(127.0.0.1:3306)/db)
+MYSQL_ADDR=$(sed -nE 's/.*store_dsn:[[:space:]]*"?[^\(]*tcp\(([^)]*)\).*/\1/p' 
"$ENGINE_CONF" | head -n1)
+MYSQL_HOST=${MYSQL_ADDR%%:*}
+MYSQL_PORT=${MYSQL_ADDR##*:}
+
+if [[ "$DO_UP" == "true" ]]; then
+  if [[ -n "${MYSQL_HOST:-}" && -n "${MYSQL_PORT:-}" ]]; then
+    wait_for_tcp "$MYSQL_HOST" "$MYSQL_PORT" "MySQL" || exit 1
+  fi
+  wait_for_tcp "$SEATA_HOST" "$SEATA_PORT" "Seata" || exit 1
+  echo "[+] Extra wait ${WAIT_READY_MARGIN}s for services to finish 
initialization ..."
+  sleep "$WAIT_READY_MARGIN"
+else
+  wait_for_tcp "$SEATA_HOST" "$SEATA_PORT" "Seata" || exit 1
+fi
+
+echo "[+] Running all scenarios via single process ..."
+OUT=$(go run ./saga/e2e -seataConf="$SEATA_CONF" -engineConf="$ENGINE_CONF" | 
tee /dev/stderr)
+
+XID1=$(echo "$OUT" | awk -F '[ =,]+' '/SCENARIO success XID=/{print $4}' | 
tail -n1)
+[[ -n "$XID1" ]] || { echo "[-] could not extract XID for success"; exit 1; }
+echo "[+] Validating DB for success (XID=$XID1) ..."
+go run ./saga/e2e/dbcheck -engine "$ENGINE_CONF" -xid "$XID1" -scenario success
+
+XID2=$(echo "$OUT" | awk -F '[ =,]+' '/SCENARIO compensate-balance XID=/{print 
$4}' | tail -n1)
+[[ -n "$XID2" ]] || { echo "[-] could not extract XID for compensate-balance"; 
exit 1; }
+echo "[+] Validating DB for compensate-balance (XID=$XID2) ..."
+go run ./saga/e2e/dbcheck -engine "$ENGINE_CONF" -xid "$XID2" -scenario 
compensate-balance
+
+XID3=$(echo "$OUT" | awk -F '[ =,]+' '/SCENARIO compensate-inventory 
XID=/{print $4}' | tail -n1)
+[[ -n "$XID3" ]] || { echo "[-] could not extract XID for 
compensate-inventory"; exit 1; }
+echo "[+] Validating DB for compensate-inventory (XID=$XID3) ..."
+go run ./saga/e2e/dbcheck -engine "$ENGINE_CONF" -xid "$XID3" -scenario 
compensate-inventory
+
+echo "[+] All e2e scenarios finished"
+exit 0
diff --git a/goimports.sh b/saga/e2e/run_compensation.sh
old mode 100644
new mode 100755
similarity index 69%
copy from goimports.sh
copy to saga/e2e/run_compensation.sh
index 4c30f1c..9b26dec
--- a/goimports.sh
+++ b/saga/e2e/run_compensation.sh
@@ -1,4 +1,4 @@
-#
+#!/usr/bin/env bash
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
 # this work for additional information regarding copyright ownership.
@@ -13,17 +13,14 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-# format go imports style
-go install -v golang.org/x/tools/cmd/goimports
-goimports  -w .
+set -euo pipefail
+
+DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
 
-# format licence style
-go install github.com/apache/skywalking-eyes/cmd/license-eye@latest
-license-eye header fix
-# check dependency licence is valid
-license-eye dependency check
+SEATA_CONF=${1:-$DIR/seatago.yaml}
+ENGINE_CONF=${2:-$DIR/config.yaml}
+SCENARIO=${3:-compensate-balance}
 
-# format go.mod
-go mod tidy
+echo "Running compensation scenario: $SCENARIO"
+go run ./saga/e2e -seataConf="$SEATA_CONF" -engineConf="$ENGINE_CONF" 
-scenario="$SCENARIO"
diff --git a/saga/e2e/scenario_comp_balance.go 
b/saga/e2e/scenario_comp_balance.go
new file mode 100644
index 0000000..a831440
--- /dev/null
+++ b/saga/e2e/scenario_comp_balance.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+func compensateBalanceParams() map[string]any {
+       // balance insufficient: u_b has 50, amount 100 -> fail -> compensate 
inventory
+       return map[string]any{
+               "businessKey": "bk_comp_bal",
+               "productId":   "p_b",
+               "count":       10,
+               "userId":      "u_b",
+               "amount":      100,
+       }
+}
diff --git a/saga/e2e/scenario_comp_inventory.go 
b/saga/e2e/scenario_comp_inventory.go
new file mode 100644
index 0000000..85d4e27
--- /dev/null
+++ b/saga/e2e/scenario_comp_inventory.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+func compensateInventoryParams() map[string]any {
+       // inventory insufficient: p_i has 100, count 200 -> fail -> no 
compensation
+       return map[string]any{
+               "businessKey": "bk_comp_inv",
+               "productId":   "p_i",
+               "count":       200,
+               "userId":      "u_i",
+               "amount":      100,
+       }
+}
diff --git a/saga/e2e/scenario_success.go b/saga/e2e/scenario_success.go
new file mode 100644
index 0000000..0fbf1c4
--- /dev/null
+++ b/saga/e2e/scenario_success.go
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+func successParams() map[string]any {
+       return map[string]any{
+               "businessKey": "bk_success",
+               "productId":   "p_s",
+               "count":       10,
+               "userId":      "u_s",
+               "amount":      100,
+       }
+}
diff --git a/goimports.sh b/saga/e2e/seatago.yaml
similarity index 58%
copy from goimports.sh
copy to saga/e2e/seatago.yaml
index 4c30f1c..95e73ab 100644
--- a/goimports.sh
+++ b/saga/e2e/seatago.yaml
@@ -1,4 +1,3 @@
-#
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
 # this work for additional information regarding copyright ownership.
@@ -13,17 +12,34 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-# format go imports style
-go install -v golang.org/x/tools/cmd/goimports
-goimports  -w .
+# Seata-go client config (placeholders; update to your environment)
+
+seata:
+  enabled: true
+  application-id: "saga-e2e-app"
+  tx-service-group: "default_tx_group"
+
+  client:
+    rm:
+      saga-branch-register-enable: true
+    tm: {}
+
+  service:
+    vgroup-mapping:
+      default_tx_group: "default"
+    grouplist:
+      default: "127.0.0.1:8091"
 
-# format licence style
-go install github.com/apache/skywalking-eyes/cmd/license-eye@latest
-license-eye header fix
-# check dependency licence is valid
-license-eye dependency check
+  transport:
+    type: TCP
+    server: NIO
+    heartbeat: true
 
-# format go.mod
-go mod tidy
+  getty:
+    reconnect-interval: 1
+    connection-num: 1
+    session:
+      compress-encoding: false
+      tcp-no-delay: true
+      keep-alive-period: 180
diff --git a/saga/e2e/sql/mysql_saga_schema.sql 
b/saga/e2e/sql/mysql_saga_schema.sql
new file mode 100644
index 0000000..ab2b1da
--- /dev/null
+++ b/saga/e2e/sql/mysql_saga_schema.sql
@@ -0,0 +1,78 @@
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- Saga MySQL schema (apply to your database)
+
+CREATE TABLE IF NOT EXISTS `seata_state_machine_def` (
+  `id` varchar(128) NOT NULL,
+  `tenant_id` varchar(32) DEFAULT NULL,
+  `app_name` varchar(64) DEFAULT NULL,
+  `name` varchar(128) NOT NULL,
+  `status` varchar(16) DEFAULT NULL,
+  `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
+  `ver` varchar(16) DEFAULT NULL,
+  `type` varchar(32) DEFAULT NULL,
+  `content` mediumtext,
+  `recover_strategy` varchar(32) DEFAULT NULL,
+  `comment_` varchar(255) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_smdef_name_tenant` (`name`,`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS `seata_state_machine_inst` (
+  `id` varchar(128) NOT NULL,
+  `machine_id` varchar(128) NOT NULL,
+  `tenant_id` varchar(32) DEFAULT NULL,
+  `parent_id` varchar(256) DEFAULT NULL,
+  `gmt_started` datetime DEFAULT CURRENT_TIMESTAMP,
+  `gmt_end` datetime DEFAULT NULL,
+  `status` varchar(16) DEFAULT NULL,
+  `compensation_status` varchar(16) DEFAULT NULL,
+  `is_running` tinyint(1) DEFAULT 0,
+  `gmt_updated` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `business_key` varchar(128) DEFAULT NULL,
+  `start_params` mediumtext,
+  `end_params` mediumtext,
+  `excep` blob,
+  PRIMARY KEY (`id`),
+  KEY `idx_sminst_machine` (`machine_id`),
+  KEY `idx_sminst_parent` (`parent_id`),
+  KEY `idx_sminst_bizkey_tenant` (`business_key`,`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS `seata_state_inst` (
+  `id` varchar(128) NOT NULL,
+  `machine_inst_id` varchar(128) NOT NULL,
+  `name` varchar(128) NOT NULL,
+  `type` varchar(32) NOT NULL,
+  `gmt_started` datetime DEFAULT CURRENT_TIMESTAMP,
+  `service_name` varchar(255) DEFAULT NULL,
+  `service_method` varchar(255) DEFAULT NULL,
+  `service_type` varchar(32) DEFAULT NULL,
+  `is_for_update` tinyint(1) DEFAULT 0,
+  `input_params` mediumtext,
+  `status` varchar(16) DEFAULT NULL,
+  `business_key` varchar(128) DEFAULT NULL,
+  `state_id_compensated_for` varchar(128) DEFAULT NULL,
+  `state_id_retried_for` varchar(128) DEFAULT NULL,
+  `output_params` mediumtext,
+  `excep` blob,
+  `gmt_end` datetime DEFAULT NULL,
+  `gmt_updated` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_stinst_machine` (`machine_inst_id`),
+  KEY `idx_stinst_name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
diff --git a/saga/e2e/statelang/reduce_inventory_and_balance.json 
b/saga/e2e/statelang/reduce_inventory_and_balance.json
new file mode 100644
index 0000000..45d9155
--- /dev/null
+++ b/saga/e2e/statelang/reduce_inventory_and_balance.json
@@ -0,0 +1,75 @@
+{
+  "Name": "ReduceInventoryAndBalance",
+  "Comment": "Reduce inventory then balance with compensation (Go e2e; 
Java-like actions)",
+  "StartState": "ReduceInventory",
+  "Version": "1.0",
+  "Persist": true,
+  "States": {
+    "ReduceInventory": {
+      "Type": "ServiceTask",
+      "ServiceType": "local",
+      "ServiceName": "inventoryAction",
+      "ServiceMethod": "Reduce",
+      "IsPersist": true,
+      "CompensateState": "CompensateReduceInventory",
+      "Input": [
+        "$CEL.elContext['context']['businessKey']",
+        "$CEL.elContext['context']['productId']",
+        "$CEL.elContext['context']['count']"
+      ],
+      "Catch": [
+        {"Exceptions": ["ERROR", "NOT_ENOUGH", "INVENTORY_NOT_ENOUGH"], 
"Next": "CompensationTrigger"}
+      ],
+      "Next": "ReduceBalance"
+    },
+    "ReduceBalance": {
+      "Type": "ServiceTask",
+      "ServiceType": "local",
+      "ServiceName": "balanceAction",
+      "ServiceMethod": "Reduce",
+      "IsPersist": true,
+      "CompensateState": "CompensateReduceBalance",
+      "Input": [
+        "$CEL.elContext['context']['businessKey']",
+        "$CEL.elContext['context']['userId']",
+        "$CEL.elContext['context']['amount']"
+      ],
+      "Catch": [
+        {"Exceptions": ["ERROR", "reduce balance failed", 
"BALANCE_NOT_ENOUGH"], "Next": "CompensationTrigger"}
+      ],
+      "Next": "Success"
+    },
+    "CompensationTrigger": {
+      "Type": "CompensationTrigger",
+      "Next": "Fail"
+    },
+    "CompensateReduceInventory": {
+      "Type": "ServiceTask",
+      "ServiceType": "local",
+      "ServiceName": "inventoryAction",
+      "ServiceMethod": "CompensateReduce",
+      "IsPersist": true,
+      "Input": [
+        "$CEL.elContext['context']['businessKey']",
+        "$CEL.elContext['context']['productId']",
+        "$CEL.elContext['context']['count']"
+      ],
+      "Next": "Success"
+    },
+    "CompensateReduceBalance": {
+      "Type": "ServiceTask",
+      "ServiceType": "local",
+      "ServiceName": "balanceAction",
+      "ServiceMethod": "CompensateReduce",
+      "IsPersist": true,
+      "Input": [
+        "$CEL.elContext['context']['businessKey']",
+        "$CEL.elContext['context']['userId']",
+        "$CEL.elContext['context']['amount']"
+      ],
+      "Next": "Success"
+    },
+    "Success": {"Type": "Succeed"},
+    "Fail": {"Type": "Fail", "Comment": "Ended with compensation or error"}
+  }
+}
diff --git a/goimports.sh b/saga/e2e/up_and_run.sh
old mode 100644
new mode 100755
similarity index 51%
copy from goimports.sh
copy to saga/e2e/up_and_run.sh
index 4c30f1c..c1730cf
--- a/goimports.sh
+++ b/saga/e2e/up_and_run.sh
@@ -1,4 +1,4 @@
-#
+#!/usr/bin/env bash
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
 # this work for additional information regarding copyright ownership.
@@ -13,17 +13,23 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-# format go imports style
-go install -v golang.org/x/tools/cmd/goimports
-goimports  -w .
+set -euo pipefail
+
+DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
+
+echo "Resetting docker-compose services (MySQL + Seata Server)..."
+docker-compose -f "$DIR/docker-compose.yml" down -v --remove-orphans || true
+docker-compose -f "$DIR/docker-compose.yml" rm -f -s -v || true
+echo "Starting docker-compose services (fresh)..."
+docker-compose -f "$DIR/docker-compose.yml" up -d --force-recreate
 
-# format licence style
-go install github.com/apache/skywalking-eyes/cmd/license-eye@latest
-license-eye header fix
-# check dependency licence is valid
-license-eye dependency check
+echo "Waiting for MySQL (3306) and Seata Server (8091) to be ready..."
+# simple wait loop
+for i in {1..60}; do
+  nc -z 127.0.0.1 3306 && nc -z 127.0.0.1 8091 && break || true
+  sleep 2
+done
 
-# format go.mod
-go mod tidy
+echo "Running saga e2e example"
+go run ./saga/e2e -seataConf="$DIR/seatago.yaml" -engineConf="$DIR/config.yaml"


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to