Skip to content

Commit d5f2aa2

Browse files
committed
Add query historizer to troubleshoot when a query takes too long.
1 parent c8e782a commit d5f2aa2

File tree

6 files changed

+94
-22
lines changed

6 files changed

+94
-22
lines changed

cmd/go-graphkb/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func listen(cmd *cobra.Command, args []string) {
123123

124124
listenInterface := viper.GetString("server_listen")
125125

126-
server.StartServer(listenInterface, Database, Database, Database, eventBus)
126+
server.StartServer(listenInterface, Database, Database, Database, Database, eventBus)
127127

128128
close(eventBus)
129129
}
@@ -142,7 +142,7 @@ func queryFunc(cmd *cobra.Command, args []string) {
142142
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
143143
defer cancel()
144144

145-
q := knowledge.NewQuerier(Database)
145+
q := knowledge.NewQuerier(Database, Database)
146146

147147
r, err := q.Query(ctx, args[0])
148148
if err != nil {

internal/database/mariadb.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212

1313
"github.com/cheggaaa/pb"
1414
"github.com/clems4ever/go-graphkb/internal/knowledge"
15-
"github.com/clems4ever/go-graphkb/internal/query"
1615
"github.com/clems4ever/go-graphkb/internal/schema"
1716
"github.com/clems4ever/go-graphkb/internal/utils"
1817
mapset "github.com/deckarep/golang-set"
@@ -111,6 +110,23 @@ CREATE TABLE IF NOT EXISTS importers (
111110
return err
112111
}
113112
defer q.Close()
113+
114+
// Create the table storing importers tokens
115+
q, err = m.db.QueryContext(context.Background(), `
116+
CREATE TABLE IF NOT EXISTS query_history (
117+
id INTEGER AUTO_INCREMENT NOT NULL,
118+
timestamp TIMESTAMP,
119+
query_cypher TEXT NOT NULL,
120+
query_sql TEXT NOT NULL,
121+
execution_time_ms INT,
122+
status ENUM('SUCCESS', 'FAILURE'),
123+
error TEXT,
124+
CONSTRAINT pk_history PRIMARY KEY (id)
125+
)`)
126+
if err != nil {
127+
return err
128+
}
129+
defer q.Close()
114130
return nil
115131
}
116132

@@ -494,12 +510,7 @@ func (m *MariaDB) Close() error {
494510
}
495511

496512
// Query the database with provided intermediate query representation
497-
func (m *MariaDB) Query(ctx context.Context, query *query.QueryCypher) (*knowledge.GraphQueryResult, error) {
498-
sql, err := knowledge.NewSQLQueryTranslator().Translate(query)
499-
if err != nil {
500-
return nil, err
501-
}
502-
513+
func (m *MariaDB) Query(ctx context.Context, sql knowledge.SQLTranslation) (*knowledge.GraphQueryResult, error) {
503514
deadline, ok := ctx.Deadline()
504515
// If there is a deadline, we make sure the query stops right after it has been reached.
505516
if ok {
@@ -522,6 +533,24 @@ func (m *MariaDB) Query(ctx context.Context, query *query.QueryCypher) (*knowled
522533
return res, nil
523534
}
524535

536+
func (m *MariaDB) SaveSuccessfulQuery(ctx context.Context, cypher, sql string, duration time.Duration) error {
537+
_, err := m.db.ExecContext(ctx, "INSERT INTO query_history (id, timestamp, query_cypher, query_sql, status, execution_time_ms) VALUES (NULL, CURRENT_TIMESTAMP(), ?, ?, 'SUCCESS', ?)",
538+
cypher, sql, duration)
539+
if err != nil {
540+
return err
541+
}
542+
return err
543+
}
544+
545+
func (m *MariaDB) SaveFailedQuery(ctx context.Context, cypher, sql string, err error) error {
546+
_, inErr := m.db.ExecContext(ctx, "INSERT INTO query_history (id, timestamp, query_cypher, query_sql, status, error) VALUES (NULL, CURRENT_TIMESTAMP(), ?, ?, 'FAILURE', ?)",
547+
cypher, sql, err.Error())
548+
if inErr != nil {
549+
return inErr
550+
}
551+
return nil
552+
}
553+
525554
// SaveSchema save the schema graph in database
526555
func (m *MariaDB) SaveSchema(ctx context.Context, sourceName string, schema schema.SchemaGraph) error {
527556
b, err := json.Marshal(schema)

internal/history/historizer.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package history
2+
3+
import (
4+
"context"
5+
"time"
6+
)
7+
8+
type Status int
9+
10+
const (
11+
Success Status = iota
12+
Failure Status = iota
13+
)
14+
15+
type Historizer interface {
16+
SaveSuccessfulQuery(ctx context.Context, cypher, sql string, duration time.Duration) error
17+
SaveFailedQuery(ctx context.Context, cypher, sql string, err error) error
18+
}

internal/knowledge/graphdb.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66

7-
"github.com/clems4ever/go-graphkb/internal/query"
87
"github.com/clems4ever/go-graphkb/internal/schema"
98
)
109

@@ -27,7 +26,7 @@ type GraphDB interface {
2726
CountAssets() (int64, error)
2827
CountRelations() (int64, error)
2928

30-
Query(ctx context.Context, query *query.QueryCypher) (*GraphQueryResult, error)
29+
Query(ctx context.Context, query SQLTranslation) (*GraphQueryResult, error)
3130
}
3231

3332
// Cursor is a cursor over the results

internal/knowledge/querier.go

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import (
55
"fmt"
66
"time"
77

8+
"github.com/clems4ever/go-graphkb/internal/history"
89
"github.com/clems4ever/go-graphkb/internal/query"
910
)
1011

1112
type Querier struct {
12-
GraphDB GraphDB
13+
GraphDB GraphDB
14+
historizer history.Historizer
1315
}
1416

1517
type QuerierResult struct {
@@ -18,31 +20,53 @@ type QuerierResult struct {
1820
Statistics Statistics
1921
}
2022

21-
func NewQuerier(db GraphDB) *Querier {
22-
return &Querier{GraphDB: db}
23+
func NewQuerier(db GraphDB, historizer history.Historizer) *Querier {
24+
return &Querier{GraphDB: db, historizer: historizer}
2325
}
2426

2527
func (q *Querier) Query(ctx context.Context, queryString string) (*QuerierResult, error) {
28+
qr, sql, err := q.queryInternal(ctx, queryString)
29+
if err != nil {
30+
saveErr := q.historizer.SaveFailedQuery(ctx, queryString, sql, err)
31+
if saveErr != nil {
32+
return nil, fmt.Errorf("Unable to save query error in database: %v", saveErr)
33+
}
34+
return nil, err
35+
}
36+
37+
saveErr := q.historizer.SaveSuccessfulQuery(ctx, queryString, sql, qr.Statistics.Execution/time.Millisecond)
38+
if saveErr != nil {
39+
return nil, fmt.Errorf("Unable to save query history in database: %v", saveErr)
40+
}
41+
return qr, nil
42+
}
43+
44+
func (q *Querier) queryInternal(ctx context.Context, cypherQuery string) (*QuerierResult, string, error) {
2645
s := Statistics{}
2746

2847
var err error
2948
var queryCypher *query.QueryCypher
3049

3150
s.Parsing = MeasureDuration(func() {
32-
queryCypher, err = query.TransformCypher(queryString)
51+
queryCypher, err = query.TransformCypher(cypherQuery)
3352
})
3453

3554
if err != nil {
36-
return nil, err
55+
return nil, "", err
56+
}
57+
58+
translation, err := NewSQLQueryTranslator().Translate(queryCypher)
59+
if err != nil {
60+
return nil, "", err
3761
}
3862

3963
var res *GraphQueryResult
4064
s.Execution = MeasureDuration(func() {
41-
res, err = q.GraphDB.Query(ctx, queryCypher)
65+
res, err = q.GraphDB.Query(ctx, *translation)
4266
})
4367

4468
if err != nil {
45-
return nil, err
69+
return nil, translation.Query, err
4670
}
4771

4872
fmt.Printf("Found results in %dms\n", s.Execution/time.Millisecond)
@@ -52,7 +76,7 @@ func (q *Querier) Query(ctx context.Context, queryString string) (*QuerierResult
5276
Projections: res.Projections,
5377
Statistics: s,
5478
}
55-
return result, nil
79+
return result, translation.Query, nil
5680
}
5781

5882
type Statistics struct {

internal/server/server.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111
"time"
1212

13+
"github.com/clems4ever/go-graphkb/internal/history"
1314
"github.com/clems4ever/go-graphkb/internal/importers"
1415

1516
auth "github.com/abbot/go-http-auth"
@@ -140,7 +141,7 @@ func getDatabaseDetails(database knowledge.GraphDB) http.HandlerFunc {
140141
}
141142
}
142143

143-
func postQuery(database knowledge.GraphDB) http.HandlerFunc {
144+
func postQuery(database knowledge.GraphDB, queryHistorizer history.Historizer) http.HandlerFunc {
144145
return func(w http.ResponseWriter, r *http.Request) {
145146
type QueryRequestBody struct {
146147
Query string `json:"q"`
@@ -174,7 +175,7 @@ func postQuery(database knowledge.GraphDB) http.HandlerFunc {
174175
return
175176
}
176177

177-
querier := knowledge.NewQuerier(database)
178+
querier := knowledge.NewQuerier(database, queryHistorizer)
178179
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
179180
defer cancel()
180181

@@ -356,14 +357,15 @@ func StartServer(listenInterface string,
356357
database knowledge.GraphDB,
357358
schemaPersistor schema.Persistor,
358359
importersRegistry importers.Registry,
360+
queryHistorizer history.Historizer,
359361
graphUpdatesC chan knowledge.SourceSubGraphUpdates) {
360362

361363
r := mux.NewRouter()
362364

363365
listImportersHandler := listImporters(importersRegistry)
364366
getSourceGraphHandler := getSourceGraph(importersRegistry, schemaPersistor)
365367
getDatabaseDetailsHandler := getDatabaseDetails(database)
366-
postQueryHandler := postQuery(database)
368+
postQueryHandler := postQuery(database, queryHistorizer)
367369
flushDatabaseHandler := flushDatabase(database)
368370

369371
if viper.GetString("password") != "" {

0 commit comments

Comments
 (0)