@@ -6,20 +6,22 @@ package cloning
6
6
7
7
import (
8
8
"context"
9
+ "database/sql"
9
10
"fmt"
10
11
"strconv"
11
12
"strings"
12
13
"sync"
13
14
"time"
14
15
16
+ _ "github.com/lib/pq" // Register Postgres database driver.
17
+ "github.com/pkg/errors"
18
+ "github.com/rs/xid"
19
+
15
20
"gitlab.com/postgres-ai/database-lab/pkg/log"
16
21
"gitlab.com/postgres-ai/database-lab/pkg/models"
17
22
"gitlab.com/postgres-ai/database-lab/pkg/services/provision"
18
23
"gitlab.com/postgres-ai/database-lab/pkg/util"
19
24
"gitlab.com/postgres-ai/database-lab/pkg/util/pglog"
20
-
21
- "github.com/pkg/errors"
22
- "github.com/rs/xid"
23
25
)
24
26
25
27
const idleCheckDuration = 5 * time .Minute
@@ -497,6 +499,7 @@ func (c *baseCloning) destroyIdleClones(ctx context.Context) {
497
499
}
498
500
}
499
501
502
+ // isIdleClone checks if clone is idle.
500
503
func (c * baseCloning ) isIdleClone (wrapper * CloneWrapper ) (bool , error ) {
501
504
currentTime := time .Now ()
502
505
@@ -520,7 +523,7 @@ func (c *baseCloning) isIdleClone(wrapper *CloneWrapper) (bool, error) {
520
523
log .Dbg (fmt .Sprintf ("Not found recent activity for the session: %q. Session name: %q" ,
521
524
session .ID , session .Name ))
522
525
523
- return true , nil
526
+ return hasNotQueryActivity ( session )
524
527
}
525
528
526
529
return false , errors .Wrap (err , "failed to get the last session activity" )
@@ -532,5 +535,42 @@ func (c *baseCloning) isIdleClone(wrapper *CloneWrapper) (bool, error) {
532
535
return false , nil
533
536
}
534
537
535
- return true , nil
538
+ return hasNotQueryActivity (session )
539
+ }
540
+
541
+ const pgDriverName = "postgres"
542
+
543
+ // hasNotQueryActivity opens connection and checks if there is no any query running by a user.
544
+ func hasNotQueryActivity (session * provision.Session ) (bool , error ) {
545
+ log .Dbg (fmt .Sprintf ("Check an active query for: %q." , session .ID ))
546
+
547
+ db , err := sql .Open (pgDriverName , getSocketConnStr (session ))
548
+
549
+ if err != nil {
550
+ return false , errors .Wrap (err , "cannot connect to database" )
551
+ }
552
+
553
+ defer func () {
554
+ if err := db .Close (); err != nil {
555
+ log .Err ("Cannot close database connection." )
556
+ }
557
+ }()
558
+
559
+ return checkActiveQueryNotExists (db )
560
+ }
561
+
562
+ // TODO(akartasov): Move the function to the provision service.
563
+ func getSocketConnStr (session * provision.Session ) string {
564
+ return fmt .Sprintf ("host=%s user=%s" , session .SocketHost , session .User )
565
+ }
566
+
567
+ // checkActiveQueryNotExists runs query to check a user activity.
568
+ func checkActiveQueryNotExists (db * sql.DB ) (bool , error ) {
569
+ var isRunningQueryNotExists bool
570
+
571
+ query := `SELECT NOT EXISTS(
572
+ SELECT * FROM pg_stat_activity WHERE state NOT ILIKE 'idle%' AND query NOT LIKE 'autovacuum: %' AND pid <> pg_backend_pid())`
573
+ err := db .QueryRow (query ).Scan (& isRunningQueryNotExists )
574
+
575
+ return isRunningQueryNotExists , err
536
576
}
0 commit comments