Browse Source

Merge pull request #439 from wooln/feature-reset-cron-time

Add reset cron time button on trans detail page.
pull/447/head
yedf2 3 years ago
committed by GitHub
parent
commit
1a456b183a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      admin/src/api/api_dtm.ts
  2. 2
      admin/src/components.d.ts
  3. 22
      admin/src/views/Dashboard/GlobalTransactions/DialogTransactionDetail.vue
  4. 5
      dtmsvr/api.go
  5. 5
      dtmsvr/api_http.go
  6. 18
      dtmsvr/storage/boltdb/boltdb.go
  7. 10
      dtmsvr/storage/redis/redis.go
  8. 15
      dtmsvr/storage/sql/sql.go
  9. 1
      dtmsvr/storage/store.go
  10. 9
      dtmsvr/trans_status.go
  11. 28
      test/api_test.go
  12. 20
      test/store_test.go

8
admin/src/api/api_dtm.ts

@ -92,6 +92,14 @@ export function getTransaction<T>(payload: {
})
}
export function resetNextCronTime(gid: string): Promise<AxiosResponse> {
return request({
url: '/api/dtmsvr/resetNextCronTime',
method: 'post',
data: { gid }
})
}
export function getDtmVersion(): Promise<AxiosResponse<any>> {
return request({
url: '/api/dtmsvr/version',

2
admin/src/components.d.ts

@ -1,6 +1,6 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/core/pull/3399
// Read more: https://github.com/vuejs/vue-next/pull/3399
import '@vue/runtime-core'
declare module '@vue/runtime-core' {

22
admin/src/views/Dashboard/GlobalTransactions/DialogTransactionDetail.vue

@ -17,7 +17,16 @@
>
<a-button danger type="default" :disabled="transaction?.status==='failed' || transaction?.status==='succeed'"
>ForceStop</a-button>
</a-popconfirm>
</a-popconfirm>
<!-- todo enable condition -->
<a-popconfirm
title="Reset next cron time to current time?"
ok-text="Yes, reset"
cancel-text="No"
class="action-button"
@confirm="handleSetNextCronTimeToNow(<string>transaction?.gid)" >
<a-button type="default">Reset next cron time</a-button>
</a-popconfirm>
<a-descriptions bordered size="small" :column="{ xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 }">
<a-descriptions-item label="Status">
<a-tag :color="transaction?.status === 'succeed' ? 'green' : 'volcano'">{{ transaction?.status }}</a-tag>
@ -31,6 +40,7 @@
<a-descriptions-item label="UpdateTime">{{ transaction?.update_time }}</a-descriptions-item>
<a-descriptions-item label="NextCronInterval">{{ transaction?.next_cron_interval }}</a-descriptions-item>
<a-descriptions-item label="NextCronTime">{{ transaction?.next_cron_time }}</a-descriptions-item>
<a-descriptions-item label="RollbackReason">{{ transaction?.rollback_reason }}</a-descriptions-item>
</a-descriptions>
<h2>Branches</h2>
<a-table :columns="columns" :data-source="dataSource" :pagination="false" :scroll="{ x: true}">
@ -51,7 +61,7 @@ import { getTransaction } from '/@/api/api_dtm'
import screenfull from '/@/components/Screenfull/index.vue'
import { useRoute } from 'vue-router';
import { string } from 'vue-types';
import { forceStopTransaction} from '/@/api/api_dtm'
import { forceStopTransaction, resetNextCronTime } from '/@/api/api_dtm'
// import VueJsonPretty from 'vue-json-pretty';
// import 'vue-json-pretty/lib/styles.css'
const route = useRoute();
@ -121,6 +131,12 @@ const handleTransactionStop = async(gid: string) => {
refresh();
}
const handleSetNextCronTimeToNow = async(gid: string) => {
await resetNextCronTime(gid);
refresh();
}
type Data = {
branches: {
gid: string
@ -144,6 +160,7 @@ type Data = {
next_cron_interval: number
next_cron_time: string
concurrent: boolean
rollback_reason: string
}
}
@ -160,6 +177,7 @@ interface Transaction {
next_cron_interval: number
next_cron_time: string
concurrent: boolean
rollback_reason: string
}
interface Branches {

5
dtmsvr/api.go

@ -86,6 +86,11 @@ func svcForceStop(t *TransGlobal) interface{} {
return nil
}
func svcResetNextCronTime(t *TransGlobal) error {
dbt := GetTransGlobal(t.Gid)
return dbt.resetNextCronTime()
}
func svcRegisterBranch(transType string, branch *TransBranch, data map[string]string) error {
branches := []TransBranch{*branch, *branch}
if transType == "tcc" {

5
dtmsvr/api_http.go

@ -40,6 +40,7 @@ func addRoute(engine *gin.Engine) {
engine.DELETE("/api/dtmsvr/topic/:topicName", dtmutil.WrapHandler2(deleteTopic))
engine.GET("/api/dtmsvr/scanKV", dtmutil.WrapHandler2(scanKV))
engine.GET("/api/dtmsvr/queryKV", dtmutil.WrapHandler2(queryKV))
engine.POST("/api/dtmsvr/resetNextCronTime", dtmutil.WrapHandler2(resetNextCronTime)) // one global trans only
// add prometheus exporter
h := promhttp.Handler()
@ -69,6 +70,10 @@ func forceStop(c *gin.Context) interface{} {
return svcForceStop(TransFromContext(c))
}
func resetNextCronTime(c *gin.Context) interface{} {
return svcResetNextCronTime(TransFromContext(c))
}
func registerBranch(c *gin.Context) interface{} {
data := map[string]string{}
err := c.BindJSON(&data)

18
dtmsvr/storage/boltdb/boltdb.go

@ -482,6 +482,24 @@ func (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount in
return
}
// ResetTransGlobalCronTime reset nextCronTime of one global trans.
func (s *Store) ResetTransGlobalCronTime(g *storage.TransGlobalStore) error {
old := g.UpdateTime
err := s.boltDb.Update(func(t *bolt.Tx) error {
g := tGetGlobal(t, g.Gid)
if g == nil || g.UpdateTime == old {
return storage.ErrNotFound
}
now := dtmutil.GetNextTime(0)
g.NextCronTime = now
g.UpdateTime = now
tPutGlobal(t, g)
return nil
})
dtmimp.E2P(err)
return err
}
// ScanKV lists KV pairs
func (s *Store) ScanKV(cat string, position *string, limit int64) []storage.KVStore {
kvs := []storage.KVStore{}

10
dtmsvr/storage/redis/redis.go

@ -330,6 +330,16 @@ return tostring(i)
return
}
// ResetTransGlobalCronTime reset nextCronTime of one global trans.
func (s *Store) ResetTransGlobalCronTime(global *storage.TransGlobalStore) error {
now := dtmutil.GetNextTime(0)
global.NextCronTime = now
global.UpdateTime = now
key := conf.Store.RedisPrefix + "_g_" + global.Gid
_, err := redisGet().Set(ctx, key, dtmimp.MustMarshalString(global), time.Duration(conf.Store.DataExpire)*time.Second).Result()
return err
}
// TouchCronTime updates cronTime
func (s *Store) TouchCronTime(global *storage.TransGlobalStore, nextCronInterval int64, nextCronTime *time.Time) {
global.UpdateTime = dtmutil.GetNextTime(0)

15
dtmsvr/storage/sql/sql.go

@ -204,6 +204,21 @@ func (s *Store) ResetCronTime(after time.Duration, limit int64) (succeedCount in
return affected, affected == limit, err
}
// ResetTransGlobalCronTime reset nextCronTime of one global trans.
func (s *Store) ResetTransGlobalCronTime(global *storage.TransGlobalStore) error {
now := getTimeStr(0)
where := map[string]string{
dtmimp.DBTypeMysql: fmt.Sprintf(`gid = '%s'`, global.Gid),
}[conf.Store.Driver]
sql := fmt.Sprintf(`UPDATE trans_global SET update_time='%s',next_cron_time='%s' WHERE %s`,
now,
now,
where)
_, err := dtmimp.DBExec(conf.Store.Driver, dbGet().ToSQLDB(), sql)
return err
}
// ScanKV lists KV pairs
func (s *Store) ScanKV(cat string, position *string, limit int64) []storage.KVStore {
kvs := []storage.KVStore{}

1
dtmsvr/storage/store.go

@ -31,6 +31,7 @@ type Store interface {
TouchCronTime(global *TransGlobalStore, nextCronInterval int64, nextCronTime *time.Time)
LockOneGlobalTrans(expireIn time.Duration) *TransGlobalStore
ResetCronTime(after time.Duration, limit int64) (succeedCount int64, hasRemaining bool, err error)
ResetTransGlobalCronTime(global *TransGlobalStore) error
ScanKV(cat string, position *string, limit int64) []KVStore
FindKV(cat, key string) []KVStore
UpdateKV(kv *KVStore) error

9
dtmsvr/trans_status.go

@ -85,6 +85,15 @@ func (t *TransGlobal) changeStatus(status string, opts ...changeStatusOption) {
t.Status = status
}
func (t *TransGlobal) resetNextCronTime() error {
err := GetStore().ResetTransGlobalCronTime(&t.TransGlobalStore)
if err != nil {
return err
}
logger.Infof("ResetTransGlobalCronTime to now ok for %s", t.TransGlobalStore.String())
return nil
}
func (t *TransGlobal) changeBranchStatus(b *TransBranch, status string, branchPos int) {
now := time.Now()
b.Status = status

28
test/api_test.go

@ -248,3 +248,31 @@ func TestAPIForceStoppedAbnormal(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, resp.StatusCode(), http.StatusConflict)
}
// func TestAPIResetNextCronTime(t *testing.T) {
// saga := genSaga(dtmimp.GetFuncName(), false, false)
// saga.Submit()
// waitTransProcessed(saga.Gid)
// assert.Equal(t, []string{StatusPrepared, StatusSucceed, StatusPrepared, StatusSucceed}, getBranchesStatus(saga.Gid))
// assert.Equal(t, StatusSucceed, getTransStatus(saga.Gid))
// gid := saga.Gid
// s := registry.GetStore()
// g := s.FindTransGlobalStore(saga.Gid)
// // reset
// resp, err := dtmcli.GetRestyClient().R().SetBody(map[string]string{
// "gid": saga.Gid,
// }).Post(dtmutil.DefaultHTTPServer + "/resetNextCronTime")
// assert.Nil(t, err)
// assert.Equal(t, resp.StatusCode(), http.StatusOK)
// // after reset assert
// g2 := s.FindTransGlobalStore(gid)
// assert.NotNil(t, g2)
// assert.Equal(t, gid, g2.Gid)
// assert.Greater(t, time.Now().Add(3*time.Second), *g2.NextCronTime)
// assert.Equal(t, g2.UpdateTime, g2.NextCronTime)
// assert.NotEqual(t, g.UpdateTime, g2.UpdateTime)
// assert.NotEqual(t, g.NextCronTime, g2.NextCronTime)
// }

20
test/store_test.go

@ -178,3 +178,23 @@ func TestUpdateBranches(t *testing.T) {
assert.Nil(t, err)
}
}
// func TestResetTransGlobalCronTime(t *testing.T) {
// gid := dtmimp.GetFuncName()
// g, _ := initTransGlobal(gid)
// s := registry.GetStore()
// g2 := s.FindTransGlobalStore(gid)
// assert.NotNil(t, g2)
// assert.Equal(t, gid, g2.Gid)
// s.ResetTransGlobalCronTime(g2)
// g2 = s.FindTransGlobalStore(gid)
// assert.NotNil(t, g2)
// assert.Equal(t, gid, g2.Gid)
// assert.Greater(t, time.Now().Add(3*time.Second), *g2.NextCronTime)
// assert.Equal(t, g2.UpdateTime, g2.NextCronTime)
// assert.NotEqual(t, g.UpdateTime, g2.UpdateTime)
// assert.NotEqual(t, g.NextCronTime, g2.NextCronTime)
// }

Loading…
Cancel
Save