Skip to content

Commit 5340cdd

Browse files
committed
Add support for pt-kill. Fixes #34
1 parent 6443dd3 commit 5340cdd

File tree

6 files changed

+261
-3
lines changed

6 files changed

+261
-3
lines changed

hack/docker/mysql-helper/docker-entrypoint.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ case "$1" in
6060
*)
6161
echo "Usage: $0 {files-config|clone|config-and-serve}"
6262
echo "Now runs your command."
63+
echo "$@"
6364

6465
exec "$@"
6566
esac

pkg/apis/mysql/v1alpha1/cluster.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package v1alpha1
1919
import (
2020
"fmt"
2121
"strconv"
22+
"strings"
2223

2324
"github.com/golang/glog"
2425
apiv1 "k8s.io/api/core/v1"
@@ -275,3 +276,43 @@ func (c *MysqlCluster) GetLabels() map[string]string {
275276
"mysql_cluster": c.Name,
276277
}
277278
}
279+
280+
func (ql *QueryLimits) GetOptions() []string {
281+
options := []string{
282+
"--print",
283+
}
284+
if ql.MaxIdleTime != nil {
285+
options = append(options, "--idle-time", fmt.Sprintf("%d", *ql.MaxIdleTime))
286+
}
287+
288+
if ql.MaxQueryTime != nil {
289+
options = append(options, "--busy-time", fmt.Sprintf("%d", *ql.MaxQueryTime))
290+
}
291+
292+
if ql.Kill != nil {
293+
options = append(options, "--victims", *ql.Kill)
294+
}
295+
296+
switch ql.KillMode {
297+
case "connection":
298+
options = append(options, "--kill")
299+
case "query":
300+
options = append(options, "--kill-query")
301+
default:
302+
options = append(options, "--kill-query")
303+
}
304+
305+
if len(ql.IgnoreDb) > 0 {
306+
options = append(options, "--ignore-db", strings.Join(ql.IgnoreDb, "|"))
307+
}
308+
309+
if len(ql.IgnoreCommand) > 0 {
310+
options = append(options, "--ignore-command", strings.Join(ql.IgnoreCommand, "|"))
311+
}
312+
313+
if len(ql.IgnoreUser) > 0 {
314+
options = append(options, "--ignore-user", strings.Join(ql.IgnoreUser, "|"))
315+
}
316+
317+
return options
318+
}

pkg/apis/mysql/v1alpha1/types.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,17 @@ type ClusterSpec struct {
8585

8686
// PVC extra specifiaction
8787
// +optional
88-
VolumeSpec `json:"volumeSpec,omitempty"`
88+
VolumeSpec VolumeSpec `json:"volumeSpec,omitempty"`
8989

9090
// MaxSlaveLatency represents the allowed latency for a slave node in
9191
// seconds. If set then the node with a latency grater than this is removed
9292
// from service.
9393
// +optional
9494
MaxSlaveLatency *int64 `json:"maxSlaveLatency,omitempty"`
95+
96+
// QueryLimits represents limits for a query
97+
// +optional
98+
QueryLimits *QueryLimits `json:"queryLimits,omitempty"`
9599
}
96100

97101
type MysqlConf map[string]string
@@ -161,6 +165,40 @@ type VolumeSpec struct {
161165
core.PersistentVolumeClaimSpec `json:",inline"`
162166
}
163167

168+
type QueryLimits struct {
169+
// MaxIdleTime match queries that have benn idle for longer then this time.
170+
// (--idle-time flag)
171+
// + optional
172+
MaxIdleTime *int `json:"maxIdleTime,omitempty"`
173+
174+
// MaxQueryTime match queries that have been running for longer then this
175+
// time. (--busy-time flag)
176+
// +optional
177+
MaxQueryTime *int `json:"maxQueryTime,omitempty"`
178+
179+
// Kill represents the mode of which the matching queries in each class will
180+
// be killed, (the --victims flag). Can be one of oldest|all|all-but-oldest
181+
// +optional
182+
Kill *string `json:"kill,omitempty"`
183+
184+
// KillMode can be: `connection` or `query`, when it's used `connection`
185+
// means that when a query is matched the connection is killed (using --kill
186+
// flag) and if it's used `query` means that the query is killed (using
187+
// --kill-query flag)
188+
KillMode string `json:"killMode,omitempty"`
189+
190+
// IgnoreDb is the lost of tatabase that are ignored by pt-kill (--ignore-db
191+
// flag)
192+
// +optional
193+
IgnoreDb []string `json:"ignoreDb,omitempty"`
194+
195+
// IgnoreCommands
196+
IgnoreCommand []string `json:"ignoreCommands,omitempty"`
197+
198+
//IgnoreUser
199+
IgnoreUser []string `json:"ignoreUser,omitempty"`
200+
}
201+
164202
// +genclient
165203
// +k8s:openapi-gen=true
166204
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

pkg/apis/mysql/v1alpha1/zz_generated.deepcopy.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) {
119119
**out = **in
120120
}
121121
}
122+
if in.QueryLimits != nil {
123+
in, out := &in.QueryLimits, &out.QueryLimits
124+
if *in == nil {
125+
*out = nil
126+
} else {
127+
*out = new(QueryLimits)
128+
(*in).DeepCopyInto(*out)
129+
}
130+
}
122131
return
123132
}
124133

@@ -372,6 +381,64 @@ func (in *PodSpec) DeepCopy() *PodSpec {
372381
return out
373382
}
374383

384+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
385+
func (in *QueryLimits) DeepCopyInto(out *QueryLimits) {
386+
*out = *in
387+
if in.MaxIdleTime != nil {
388+
in, out := &in.MaxIdleTime, &out.MaxIdleTime
389+
if *in == nil {
390+
*out = nil
391+
} else {
392+
*out = new(int)
393+
**out = **in
394+
}
395+
}
396+
if in.MaxQueryTime != nil {
397+
in, out := &in.MaxQueryTime, &out.MaxQueryTime
398+
if *in == nil {
399+
*out = nil
400+
} else {
401+
*out = new(int)
402+
**out = **in
403+
}
404+
}
405+
if in.Kill != nil {
406+
in, out := &in.Kill, &out.Kill
407+
if *in == nil {
408+
*out = nil
409+
} else {
410+
*out = new(string)
411+
**out = **in
412+
}
413+
}
414+
if in.IgnoreDb != nil {
415+
in, out := &in.IgnoreDb, &out.IgnoreDb
416+
*out = make([]string, len(*in))
417+
copy(*out, *in)
418+
}
419+
if in.IgnoreCommand != nil {
420+
in, out := &in.IgnoreCommand, &out.IgnoreCommand
421+
*out = make([]string, len(*in))
422+
copy(*out, *in)
423+
}
424+
if in.IgnoreUser != nil {
425+
in, out := &in.IgnoreUser, &out.IgnoreUser
426+
*out = make([]string, len(*in))
427+
copy(*out, *in)
428+
}
429+
return
430+
}
431+
432+
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QueryLimits.
433+
func (in *QueryLimits) DeepCopy() *QueryLimits {
434+
if in == nil {
435+
return nil
436+
}
437+
out := new(QueryLimits)
438+
in.DeepCopyInto(out)
439+
return out
440+
}
441+
375442
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
376443
func (in *VolumeSpec) DeepCopyInto(out *VolumeSpec) {
377444
*out = *in

pkg/mysqlcluster/statefullset.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ const (
116116
containerMysqlName = "mysql"
117117
containerExporterName = "metrics-exporter"
118118
containerHeartBeatName = "pt-heartbeat"
119+
containerKillerName = "pt-kill"
119120
)
120121

121122
func (f *cFactory) ensureContainer(in core.Container, name, image string, args []string) core.Container {
@@ -287,6 +288,10 @@ func (f *cFactory) ensureInitContainersSpec(in []core.Container) []core.Containe
287288

288289
func (f *cFactory) ensureContainersSpec(in []core.Container) []core.Container {
289290
noContainers := 4
291+
if f.cluster.Spec.QueryLimits != nil {
292+
noContainers += 1
293+
}
294+
290295
if len(in) != noContainers {
291296
in = make([]core.Container, noContainers)
292297
}
@@ -382,6 +387,23 @@ func (f *cFactory) ensureContainersSpec(in []core.Container) []core.Container {
382387

383388
in[3] = heartbeat
384389

390+
if f.cluster.Spec.QueryLimits != nil {
391+
command := []string{
392+
"pt-kill",
393+
"--wait-after-kill=1",
394+
// host need to be specified, see pt-kill bug: https://jira.percona.com/browse/PT-1223
395+
"--host=127.0.0.1",
396+
fmt.Sprintf("--defaults-file=%s/client.cnf", ConfVolumeMountPath),
397+
}
398+
command = append(command, f.cluster.Spec.QueryLimits.GetOptions()...)
399+
400+
killer := f.ensureContainer(in[4], containerKillerName,
401+
f.cluster.Spec.GetHelperImage(),
402+
command,
403+
)
404+
in[4] = killer
405+
}
406+
385407
return in
386408
}
387409

@@ -477,7 +499,7 @@ func (f *cFactory) getVolumeMountsFor(name string) []core.VolumeMount {
477499
},
478500
}
479501

480-
case containerHeartBeatName:
502+
case containerHeartBeatName, containerKillerName:
481503
return []core.VolumeMount{
482504
core.VolumeMount{
483505
Name: confVolumeName,

pkg/openapi/openapi_generated.go

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
4141
"github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.NodeCondition": schema_pkg_apis_mysql_v1alpha1_NodeCondition(ref),
4242
"github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.NodeStatus": schema_pkg_apis_mysql_v1alpha1_NodeStatus(ref),
4343
"github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.PodSpec": schema_pkg_apis_mysql_v1alpha1_PodSpec(ref),
44+
"github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.QueryLimits": schema_pkg_apis_mysql_v1alpha1_QueryLimits(ref),
4445
"github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.VolumeSpec": schema_pkg_apis_mysql_v1alpha1_VolumeSpec(ref),
4546
"k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref),
4647
"k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref),
@@ -343,11 +344,17 @@ func schema_pkg_apis_mysql_v1alpha1_ClusterSpec(ref common.ReferenceCallback) co
343344
Format: "int64",
344345
},
345346
},
347+
"queryLimits": {
348+
SchemaProps: spec.SchemaProps{
349+
Description: "QueryLimits represents limits for a query",
350+
Ref: ref("github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.QueryLimits"),
351+
},
352+
},
346353
},
347354
},
348355
},
349356
Dependencies: []string{
350-
"github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.PodSpec", "github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.VolumeSpec"},
357+
"github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.PodSpec", "github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.QueryLimits", "github.com/presslabs/mysql-operator/pkg/apis/mysql/v1alpha1.VolumeSpec"},
351358
}
352359
}
353360

@@ -669,6 +676,88 @@ func schema_pkg_apis_mysql_v1alpha1_PodSpec(ref common.ReferenceCallback) common
669676
}
670677
}
671678

679+
func schema_pkg_apis_mysql_v1alpha1_QueryLimits(ref common.ReferenceCallback) common.OpenAPIDefinition {
680+
return common.OpenAPIDefinition{
681+
Schema: spec.Schema{
682+
SchemaProps: spec.SchemaProps{
683+
Properties: map[string]spec.Schema{
684+
"maxIdleTime": {
685+
SchemaProps: spec.SchemaProps{
686+
Description: "MaxIdleTime match queries that have benn idle for longer then this time. (--idle-time flag)",
687+
Type: []string{"integer"},
688+
Format: "int32",
689+
},
690+
},
691+
"maxQueryTime": {
692+
SchemaProps: spec.SchemaProps{
693+
Description: "MaxQueryTime match queries that have been running for longer then this time. (--busy-time flag)",
694+
Type: []string{"integer"},
695+
Format: "int32",
696+
},
697+
},
698+
"kill": {
699+
SchemaProps: spec.SchemaProps{
700+
Description: "Kill represents the mode of which the matching queries in each class will be killed, (the --victims flag). Can be one of oldest|all|all-but-oldest",
701+
Type: []string{"string"},
702+
Format: "",
703+
},
704+
},
705+
"killMode": {
706+
SchemaProps: spec.SchemaProps{
707+
Description: "KillMode can be: `connection` or `query`, when it's used `connection` means that when a query is matched the connection is killed (using --kill flag) and if it's used `query` means that the query is killed (using --kill-query flag)",
708+
Type: []string{"string"},
709+
Format: "",
710+
},
711+
},
712+
"ignoreDb": {
713+
SchemaProps: spec.SchemaProps{
714+
Description: "IgnoreDb is the lost of tatabase that are ignored by pt-kill (--ignore-db flag)",
715+
Type: []string{"array"},
716+
Items: &spec.SchemaOrArray{
717+
Schema: &spec.Schema{
718+
SchemaProps: spec.SchemaProps{
719+
Type: []string{"string"},
720+
Format: "",
721+
},
722+
},
723+
},
724+
},
725+
},
726+
"ignoreCommands": {
727+
SchemaProps: spec.SchemaProps{
728+
Description: "IgnoreCommands",
729+
Type: []string{"array"},
730+
Items: &spec.SchemaOrArray{
731+
Schema: &spec.Schema{
732+
SchemaProps: spec.SchemaProps{
733+
Type: []string{"string"},
734+
Format: "",
735+
},
736+
},
737+
},
738+
},
739+
},
740+
"ignoreUser": {
741+
SchemaProps: spec.SchemaProps{
742+
Description: "IgnoreUser",
743+
Type: []string{"array"},
744+
Items: &spec.SchemaOrArray{
745+
Schema: &spec.Schema{
746+
SchemaProps: spec.SchemaProps{
747+
Type: []string{"string"},
748+
Format: "",
749+
},
750+
},
751+
},
752+
},
753+
},
754+
},
755+
},
756+
},
757+
Dependencies: []string{},
758+
}
759+
}
760+
672761
func schema_pkg_apis_mysql_v1alpha1_VolumeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
673762
return common.OpenAPIDefinition{
674763
Schema: spec.Schema{

0 commit comments

Comments
 (0)