Skip to content

Commit ca79876

Browse files
authored
fix: widen unbounded GitHub and CI/CD string columns (#9063)
* fix: add blueprint_id index to _devlake_pipelines GET /blueprints/:blueprintId/pipelines runs COUNT(*) and a filtered SELECT on _devlake_pipelines.blueprint_id, but the column was unindexed. On instances with tens of thousands of pipeline rows this forces a full table scan on every request; observed ~30s for a table of 34k rows, causing upstream request timeouts in config-ui. Add a gorm index tag on Pipeline.BlueprintId for fresh installs and a migration script to add the index to existing installs. Signed-off-by: Dan Crews <crewsd@gmail.com> * fix(github): widen unbounded tool fields Signed-off-by: Dan Crews <crewsd@gmail.com> * fix(domain): widen unbounded CI/CD fields Signed-off-by: Dan Crews <crewsd@gmail.com> --------- Signed-off-by: Dan Crews <crewsd@gmail.com>
1 parent c5a4fb6 commit ca79876

22 files changed

Lines changed: 521 additions & 24 deletions

backend/core/models/domainlayer/devops/cicd_deployment_commit.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type CicdDeploymentCommit struct {
2525
domainlayer.DomainEntity
2626
CicdScopeId string `gorm:"index;type:varchar(255)"`
2727
CicdDeploymentId string `gorm:"type:varchar(255)"` // if it is converted from a cicd_pipeline_commit
28-
Name string `gorm:"type:varchar(255)"`
28+
Name string `gorm:"type:text"`
2929
DisplayTitle string
3030
Url string
3131
Result string `gorm:"type:varchar(100)"`
@@ -39,11 +39,11 @@ type CicdDeploymentCommit struct {
3939
QueuedDurationSec *float64
4040
CommitSha string `gorm:"primaryKey;type:varchar(255)"`
4141
CommitMsg string
42-
RefName string `gorm:"type:varchar(255)"` // to delete?
42+
RefName string `gorm:"type:text"` // to delete?
4343
RepoId string `gorm:"type:varchar(255)"`
4444
RepoUrl string `gorm:"index;not null"`
4545
PrevSuccessDeploymentCommitId string `gorm:"type:varchar(255)"`
46-
SubtaskName string `gorm:"type:varchar(255)"`
46+
SubtaskName string `gorm:"type:text"`
4747
}
4848

4949
func (cicdDeploymentCommit CicdDeploymentCommit) TableName() string {

backend/core/models/domainlayer/devops/cicd_pipeline.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import (
2727

2828
type CICDPipeline struct {
2929
domainlayer.DomainEntity
30-
Name string `gorm:"type:varchar(255)"`
30+
Name string `gorm:"type:text"`
3131
DisplayTitle string
3232
Url string
3333
Result string `gorm:"type:varchar(100)"`

backend/core/models/domainlayer/devops/cicd_release.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ type CicdRelease struct {
2828

2929
CicdScopeId string `gorm:"index;type:varchar(255)"`
3030

31-
Name string `gorm:"type:varchar(255)"`
32-
DisplayTitle string `gorm:"type:varchar(255)"`
31+
Name string `gorm:"type:text"`
32+
DisplayTitle string `gorm:"type:text"`
3333
Description string `json:"description"`
3434
URL string `json:"url"`
3535

backend/core/models/domainlayer/devops/cicd_scope.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var _ plugin.Scope = (*CicdScope)(nil)
2828

2929
type CicdScope struct {
3030
domainlayer.DomainEntity
31-
Name string `gorm:"type:varchar(255)"`
31+
Name string `gorm:"type:text"`
3232
Description string
3333
Url string `gorm:"type:varchar(255)"`
3434
CreatedDate *time.Time

backend/core/models/domainlayer/devops/cicd_task.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const ENV_NAME_PATTERN = "ENV_NAME_PATTERN"
3838

3939
type CICDTask struct {
4040
domainlayer.DomainEntity
41-
Name string `gorm:"type:varchar(255)"`
41+
Name string `gorm:"type:text"`
4242
PipelineId string `gorm:"index;type:varchar(255)"`
4343
Result string `gorm:"type:varchar(100)"`
4444
Status string `gorm:"type:varchar(100)"`
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package devops
19+
20+
import (
21+
"reflect"
22+
"strings"
23+
"testing"
24+
)
25+
26+
func TestUnboundedStringFieldsUseText(t *testing.T) {
27+
tests := []struct {
28+
model any
29+
field string
30+
}{
31+
{CICDTask{}, "Name"},
32+
{CicdScope{}, "Name"},
33+
{CicdRelease{}, "Name"},
34+
{CicdRelease{}, "DisplayTitle"},
35+
{CicdDeploymentCommit{}, "Name"},
36+
{CicdDeploymentCommit{}, "SubtaskName"},
37+
{CicdDeploymentCommit{}, "RefName"},
38+
{CICDPipeline{}, "Name"},
39+
}
40+
41+
for _, test := range tests {
42+
modelType := reflect.TypeOf(test.model)
43+
field, found := modelType.FieldByName(test.field)
44+
if !found {
45+
t.Fatalf("%s.%s not found", modelType.Name(), test.field)
46+
}
47+
if !strings.Contains(field.Tag.Get("gorm"), "type:text") {
48+
t.Errorf("%s.%s gorm tag = %q, want type:text", modelType.Name(), test.field, field.Tag.Get("gorm"))
49+
}
50+
}
51+
}

backend/core/models/domainlayer/ticket/incident.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type Incident struct {
4646
Priority string `gorm:"type:varchar(255)"`
4747
Severity string `gorm:"type:varchar(255)"`
4848
Urgency string `gorm:"type:varchar(255)"`
49-
Component string `gorm:"type:varchar(255)"`
49+
Component string `gorm:"type:text"`
5050
OriginalProject string `gorm:"type:varchar(255)"`
5151
Table string `gorm:"index:idx_table_scope_id;type:varchar(255)"`
5252
ScopeId string `gorm:"index:idx_table_scope_id;type:varchar(255)"`
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package ticket
19+
20+
import (
21+
"reflect"
22+
"strings"
23+
"testing"
24+
)
25+
26+
func TestIncidentComponentUsesText(t *testing.T) {
27+
field, found := reflect.TypeOf(Incident{}).FieldByName("Component")
28+
if !found {
29+
t.Fatal("Incident.Component not found")
30+
}
31+
if !strings.Contains(field.Tag.Get("gorm"), "type:text") {
32+
t.Fatalf("Incident.Component gorm tag = %q, want type:text", field.Tag.Get("gorm"))
33+
}
34+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package migrationscripts
19+
20+
import (
21+
"github.com/apache/incubator-devlake/core/context"
22+
"github.com/apache/incubator-devlake/core/errors"
23+
"github.com/apache/incubator-devlake/helpers/migrationhelper"
24+
)
25+
26+
type addBlueprintIdIndexToPipelines struct{}
27+
28+
type pipeline20260818 struct {
29+
BlueprintId uint64 `gorm:"index"`
30+
}
31+
32+
func (pipeline20260818) TableName() string {
33+
return "_devlake_pipelines"
34+
}
35+
36+
func (u *addBlueprintIdIndexToPipelines) Up(basicRes context.BasicRes) errors.Error {
37+
return migrationhelper.AutoMigrateTables(basicRes, &pipeline20260818{})
38+
}
39+
40+
func (*addBlueprintIdIndexToPipelines) Version() uint64 {
41+
return 20260818000001
42+
}
43+
44+
func (*addBlueprintIdIndexToPipelines) Name() string {
45+
return "add blueprint_id index for _devlake_pipelines"
46+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package migrationscripts
19+
20+
import (
21+
"github.com/apache/incubator-devlake/core/context"
22+
"github.com/apache/incubator-devlake/core/errors"
23+
"github.com/apache/incubator-devlake/core/plugin"
24+
)
25+
26+
var _ plugin.MigrationScript = (*expandDomainTextColumns)(nil)
27+
28+
type expandDomainTextColumns struct{}
29+
30+
func (*expandDomainTextColumns) Up(basicRes context.BasicRes) errors.Error {
31+
db := basicRes.GetDal()
32+
columns := []struct {
33+
tableName string
34+
columnName string
35+
}{
36+
{"cicd_tasks", "name"},
37+
{"cicd_scopes", "name"},
38+
{"cicd_releases", "name"},
39+
{"cicd_releases", "display_title"},
40+
{"cicd_deployment_commits", "name"},
41+
{"cicd_deployment_commits", "subtask_name"},
42+
{"cicd_deployment_commits", "ref_name"},
43+
{"incidents", "component"},
44+
}
45+
for _, column := range columns {
46+
if err := db.ModifyColumnType(column.tableName, column.columnName, "text"); err != nil {
47+
return err
48+
}
49+
}
50+
return nil
51+
}
52+
53+
func (*expandDomainTextColumns) Version() uint64 {
54+
return 20260819000001
55+
}
56+
57+
func (*expandDomainTextColumns) Name() string {
58+
return "expand domain text columns"
59+
}

0 commit comments

Comments
 (0)