Skip to content

Commit a64e23d

Browse files
authored
ddl: limit the count of getting ddlhistory jobs (#55590) (#56141)
close #55711
1 parent ed63cd2 commit a64e23d

File tree

6 files changed

+163
-11
lines changed

6 files changed

+163
-11
lines changed

docs/tidb_http_api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,12 +458,12 @@ timezone.*
458458

459459
**Note**: If you request a TiDB that is not ddl owner, the response will be `This node is not a ddl owner, can't be resigned.`
460460
461-
1. Get all TiDB DDL job history information.
461+
1. Get the TiDB DDL job history information.
462462
463463
```shell
464464
curl http://{TiDBIP}:10080/ddl/history
465465
```
466-
**Note**: When the DDL history is very very long, it may consume a lot memory and even cause OOM. Consider adding `start_job_id` and `limit`.
466+
**Note**: When the DDL history is very very long, system table may containg too many jobs. This interface will get a maximum of 2048 history ddl jobs by default. If you want get more jobs, consider adding `start_job_id` and `limit`.
467467
468468
1. Get count {number} TiDB DDL job history information.
469469

pkg/ddl/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ go_test(
201201
"ddl_algorithm_test.go",
202202
"ddl_api_test.go",
203203
"ddl_error_test.go",
204+
"ddl_history_test.go",
204205
"ddl_running_jobs_test.go",
205206
"ddl_test.go",
206207
"ddl_worker_test.go",

pkg/ddl/ddl.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,6 +1708,9 @@ const DefNumHistoryJobs = 10
17081708

17091709
const batchNumHistoryJobs = 128
17101710

1711+
// DefNumGetDDLHistoryJobs is the max count for getting the ddl history once.
1712+
const DefNumGetDDLHistoryJobs = 2048
1713+
17111714
// GetLastNHistoryDDLJobs returns the DDL history jobs and an error.
17121715
// The maximum count of history jobs is num.
17131716
func GetLastNHistoryDDLJobs(t *meta.Meta, maxNumJobs int) ([]*model.Job, error) {
@@ -1788,9 +1791,23 @@ func GetAllHistoryDDLJobs(m *meta.Meta) ([]*model.Job, error) {
17881791
func ScanHistoryDDLJobs(m *meta.Meta, startJobID int64, limit int) ([]*model.Job, error) {
17891792
var iter meta.LastJobIterator
17901793
var err error
1794+
17911795
if startJobID == 0 {
1796+
// if 'start_job_id' == 0 and 'limit' == 0(default value), get the last 1024 ddl history job by defaultly.
1797+
if limit == 0 {
1798+
limit = DefNumGetDDLHistoryJobs
1799+
1800+
failpoint.Inject("history-ddl-jobs-limit", func(val failpoint.Value) {
1801+
injectLimit, ok := val.(int)
1802+
if ok {
1803+
logutil.BgLogger().Info("failpoint history-ddl-jobs-limit", zap.Int("limit", injectLimit))
1804+
limit = injectLimit
1805+
}
1806+
})
1807+
}
17921808
iter, err = m.GetLastHistoryDDLJobsIterator()
17931809
} else {
1810+
// if 'start_job_id' > 0, it must set value to 'limit'
17941811
if limit == 0 {
17951812
return nil, errors.New("when 'start_job_id' is specified, it must work with a 'limit'")
17961813
}
@@ -1799,6 +1816,7 @@ func ScanHistoryDDLJobs(m *meta.Meta, startJobID int64, limit int) ([]*model.Job
17991816
if err != nil {
18001817
return nil, errors.Trace(err)
18011818
}
1819+
18021820
return iter.GetLastJobs(limit, nil)
18031821
}
18041822

pkg/ddl/ddl_history_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright 2024 PingCAP, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package ddl_test
16+
17+
import (
18+
"context"
19+
"testing"
20+
21+
"github.com/ngaut/pools"
22+
"github.com/pingcap/failpoint"
23+
"github.com/pingcap/tidb/pkg/ddl"
24+
"github.com/pingcap/tidb/pkg/ddl/internal/session"
25+
"github.com/pingcap/tidb/pkg/kv"
26+
"github.com/pingcap/tidb/pkg/meta"
27+
"github.com/pingcap/tidb/pkg/parser/model"
28+
"github.com/pingcap/tidb/pkg/testkit"
29+
"github.com/stretchr/testify/require"
30+
)
31+
32+
func TestDDLHistoryBasic(t *testing.T) {
33+
var (
34+
ddlHistoryJobCount = 0
35+
)
36+
37+
store := testkit.CreateMockStore(t)
38+
rs := pools.NewResourcePool(func() (pools.Resource, error) {
39+
newTk := testkit.NewTestKit(t, store)
40+
return newTk.Session(), nil
41+
}, 8, 8, 0)
42+
sessPool := session.NewSessionPool(rs, store)
43+
sessCtx, err := sessPool.Get()
44+
require.NoError(t, err)
45+
sess := session.NewSession(sessCtx)
46+
ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnLightning)
47+
err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error {
48+
t := meta.NewMeta(txn)
49+
return ddl.AddHistoryDDLJob(sess, t, &model.Job{
50+
ID: 1,
51+
}, false)
52+
})
53+
require.NoError(t, err)
54+
err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error {
55+
t := meta.NewMeta(txn)
56+
return ddl.AddHistoryDDLJob(sess, t, &model.Job{
57+
ID: 2,
58+
}, false)
59+
})
60+
require.NoError(t, err)
61+
job, err := ddl.GetHistoryJobByID(sessCtx, 1)
62+
require.NoError(t, err)
63+
require.Equal(t, int64(1), job.ID)
64+
err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error {
65+
m := meta.NewMeta(txn)
66+
jobs, err := ddl.GetLastNHistoryDDLJobs(m, 2)
67+
require.NoError(t, err)
68+
require.Equal(t, 2, len(jobs))
69+
return nil
70+
})
71+
require.NoError(t, err)
72+
73+
err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error {
74+
m := meta.NewMeta(txn)
75+
jobs, err := ddl.GetAllHistoryDDLJobs(m)
76+
require.NoError(t, err)
77+
ddlHistoryJobCount = len(jobs)
78+
return nil
79+
})
80+
81+
require.NoError(t, err)
82+
err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error {
83+
m := meta.NewMeta(txn)
84+
jobs, err := ddl.ScanHistoryDDLJobs(m, 2, 2)
85+
require.NoError(t, err)
86+
require.Equal(t, 2, len(jobs))
87+
require.Equal(t, int64(2), jobs[0].ID)
88+
require.Equal(t, int64(1), jobs[1].ID)
89+
return nil
90+
})
91+
92+
require.NoError(t, err)
93+
94+
require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/history-ddl-jobs-limit", "return(128)"))
95+
defer func() {
96+
require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/history-ddl-jobs-limit"))
97+
}()
98+
99+
err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error {
100+
m := meta.NewMeta(txn)
101+
jobs, err := ddl.ScanHistoryDDLJobs(m, 0, 0)
102+
require.NoError(t, err)
103+
if ddlHistoryJobCount <= 128 {
104+
require.Equal(t, ddlHistoryJobCount, len(jobs))
105+
} else {
106+
require.Equal(t, 128, len(jobs))
107+
}
108+
require.True(t, len(jobs) > 2)
109+
require.Equal(t, int64(2), jobs[ddlHistoryJobCount-2].ID)
110+
require.Equal(t, int64(1), jobs[ddlHistoryJobCount-1].ID)
111+
return nil
112+
})
113+
114+
require.NoError(t, err)
115+
}
116+
117+
func TestScanHistoryDDLJobsWithErrorLimit(t *testing.T) {
118+
var (
119+
m = &meta.Meta{}
120+
startJobID int64 = 10
121+
limit = 0
122+
)
123+
124+
_, err := ddl.ScanHistoryDDLJobs(m, startJobID, limit)
125+
require.ErrorContains(t, err, "when 'start_job_id' is specified, it must work with a 'limit'")
126+
}

pkg/server/handler/tests/http_handler_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package tests
1616

1717
import (
1818
"bytes"
19+
"cmp"
1920
"context"
2021
"crypto/tls"
2122
"crypto/x509"
@@ -29,6 +30,7 @@ import (
2930
"net/http"
3031
"net/http/httptest"
3132
"net/http/httputil"
33+
"slices"
3234
"sort"
3335
"testing"
3436
"time"
@@ -992,6 +994,11 @@ func TestAllHistory(t *testing.T) {
992994
data, err := ddl.GetAllHistoryDDLJobs(txnMeta)
993995
require.NoError(t, err)
994996
err = decoder.Decode(&jobs)
997+
require.True(t, len(jobs) < ddl.DefNumGetDDLHistoryJobs)
998+
// sort job.
999+
slices.SortFunc(jobs, func(i, j *model.Job) int {
1000+
return cmp.Compare(i.ID, j.ID)
1001+
})
9951002

9961003
require.NoError(t, err)
9971004
require.NoError(t, resp.Body.Close())

pkg/server/handler/tikvhandler/tikv_handler.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,8 +1089,11 @@ func (h *TableHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
10891089

10901090
// ServeHTTP handles request of ddl jobs history.
10911091
func (h DDLHistoryJobHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
1092-
var jobID, limitID int
1093-
var err error
1092+
var (
1093+
jobID = 0
1094+
limitID = 0
1095+
err error
1096+
)
10941097
if jobValue := req.FormValue(handler.JobID); len(jobValue) > 0 {
10951098
jobID, err = strconv.Atoi(jobValue)
10961099
if err != nil {
@@ -1108,8 +1111,9 @@ func (h DDLHistoryJobHandler) ServeHTTP(w http.ResponseWriter, req *http.Request
11081111
writeError(w, err)
11091112
return
11101113
}
1111-
if limitID < 1 {
1112-
writeError(w, errors.New("ddl history limit must be greater than 0"))
1114+
if limitID < 1 || limitID > ddl.DefNumGetDDLHistoryJobs {
1115+
handler.WriteError(w,
1116+
errors.Errorf("ddl history limit must be greater than 0 and less than or equal to %v", ddl.DefNumGetDDLHistoryJobs))
11131117
return
11141118
}
11151119
}
@@ -1129,11 +1133,7 @@ func (h DDLHistoryJobHandler) getHistoryDDL(jobID, limit int) (jobs []*model.Job
11291133
}
11301134
txnMeta := meta.NewMeta(txn)
11311135

1132-
if jobID == 0 && limit == 0 {
1133-
jobs, err = ddl.GetAllHistoryDDLJobs(txnMeta)
1134-
} else {
1135-
jobs, err = ddl.ScanHistoryDDLJobs(txnMeta, int64(jobID), limit)
1136-
}
1136+
jobs, err = ddl.ScanHistoryDDLJobs(txnMeta, int64(jobID), limit)
11371137
if err != nil {
11381138
return nil, errors.Trace(err)
11391139
}

0 commit comments

Comments
 (0)