Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3a9270d

Browse files
authored
chore: add PGLocks query to analyze what locks are held in pg (#15308)
adds `PGLocks` query for debugging what pg_locks are held during transactions.
1 parent6ce8bfe commit3a9270d

File tree

8 files changed

+162
-1
lines changed

8 files changed

+162
-1
lines changed

‎coderd/database/db.go‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Store interface {
2828
wrapper
2929

3030
Ping(ctx context.Context) (time.Duration,error)
31+
PGLocks(ctx context.Context) (PGLocks,error)
3132
InTx(func(Store)error,*TxOptions)error
3233
}
3334

@@ -218,3 +219,10 @@ func (q *sqlQuerier) runTx(function func(Store) error, txOpts *sql.TxOptions) er
218219
}
219220
returnnil
220221
}
222+
223+
funcsafeString(s*string)string {
224+
ifs==nil {
225+
return"<nil>"
226+
}
227+
return*s
228+
}

‎coderd/database/dbauthz/dbauthz.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,10 @@ func (q *querier) Ping(ctx context.Context) (time.Duration, error) {
603603
returnq.db.Ping(ctx)
604604
}
605605

606+
func (q*querier)PGLocks(ctx context.Context) (database.PGLocks,error) {
607+
returnq.db.PGLocks(ctx)
608+
}
609+
606610
// InTx runs the given function in a transaction.
607611
func (q*querier)InTx(functionfunc(querier database.Store)error,txOpts*database.TxOptions)error {
608612
returnq.db.InTx(func(tx database.Store)error {

‎coderd/database/dbauthz/dbauthz_test.go‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,10 @@ func TestDBAuthzRecursive(t *testing.T) {
152152
fori:=2;i<method.Type.NumIn();i++ {
153153
ins=append(ins,reflect.New(method.Type.In(i)).Elem())
154154
}
155-
ifmethod.Name=="InTx"||method.Name=="Ping"||method.Name=="Wrappers" {
155+
ifmethod.Name=="InTx"||
156+
method.Name=="Ping"||
157+
method.Name=="Wrappers"||
158+
method.Name=="PGLocks" {
156159
continue
157160
}
158161
// Log the name of the last method, so if there is a panic, it is

‎coderd/database/dbauthz/setup_test.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ var errMatchAny = xerrors.New("match any error")
3434
varskipMethods=map[string]string{
3535
"InTx":"Not relevant",
3636
"Ping":"Not relevant",
37+
"PGLocks":"Not relevant",
3738
"Wrappers":"Not relevant",
3839
"AcquireLock":"Not relevant",
3940
"TryAcquireLock":"Not relevant",

‎coderd/database/dbmem/dbmem.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,10 @@ func (*FakeQuerier) Ping(_ context.Context) (time.Duration, error) {
339339
return0,nil
340340
}
341341

342+
func (*FakeQuerier)PGLocks(_ context.Context) (database.PGLocks,error) {
343+
return []database.PGLock{},nil
344+
}
345+
342346
func (tx*fakeTx)AcquireLock(_ context.Context,idint64)error {
343347
if_,ok:=tx.FakeQuerier.locks[id];ok {
344348
returnxerrors.Errorf("cannot acquire lock %d: already held",id)

‎coderd/database/dbmetrics/querymetrics.go‎

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/dbmock/dbmock.go‎

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/pglocks.go‎

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package database
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"reflect"
7+
"sort"
8+
"strings"
9+
"time"
10+
11+
"github.com/jmoiron/sqlx"
12+
13+
"github.com/coder/coder/v2/coderd/util/slice"
14+
)
15+
16+
// PGLock docs see: https://www.postgresql.org/docs/current/view-pg-locks.html#VIEW-PG-LOCKS
17+
typePGLockstruct {
18+
// LockType see: https://www.postgresql.org/docs/current/monitoring-stats.html#WAIT-EVENT-LOCK-TABLE
19+
LockType*string`db:"locktype"`
20+
Database*string`db:"database"`// oid
21+
Relation*string`db:"relation"`// oid
22+
RelationName*string`db:"relation_name"`
23+
Page*int`db:"page"`
24+
Tuple*int`db:"tuple"`
25+
VirtualXID*string`db:"virtualxid"`
26+
TransactionID*string`db:"transactionid"`// xid
27+
ClassID*string`db:"classid"`// oid
28+
ObjID*string`db:"objid"`// oid
29+
ObjSubID*int`db:"objsubid"`
30+
VirtualTransaction*string`db:"virtualtransaction"`
31+
PIDint`db:"pid"`
32+
Mode*string`db:"mode"`
33+
Grantedbool`db:"granted"`
34+
FastPath*bool`db:"fastpath"`
35+
WaitStart*time.Time`db:"waitstart"`
36+
}
37+
38+
func (lPGLock)Equal(bPGLock)bool {
39+
// Lazy, but hope this works
40+
returnreflect.DeepEqual(l,b)
41+
}
42+
43+
func (lPGLock)String()string {
44+
granted:="granted"
45+
if!l.Granted {
46+
granted="waiting"
47+
}
48+
vardetailsstring
49+
switchsafeString(l.LockType) {
50+
case"relation":
51+
details=""
52+
case"page":
53+
details=fmt.Sprintf("page=%d",*l.Page)
54+
case"tuple":
55+
details=fmt.Sprintf("page=%d tuple=%d",*l.Page,*l.Tuple)
56+
case"virtualxid":
57+
details="waiting to acquire virtual tx id lock"
58+
default:
59+
details="???"
60+
}
61+
returnfmt.Sprintf("%d-%5s [%s] %s/%s/%s: %s",
62+
l.PID,
63+
safeString(l.TransactionID),
64+
granted,
65+
safeString(l.RelationName),
66+
safeString(l.LockType),
67+
safeString(l.Mode),
68+
details,
69+
)
70+
}
71+
72+
// PGLocks returns a list of all locks in the database currently in use.
73+
func (q*sqlQuerier)PGLocks(ctx context.Context) (PGLocks,error) {
74+
rows,err:=q.sdb.QueryContext(ctx,`
75+
SELECT
76+
relation::regclass AS relation_name,
77+
*
78+
FROM pg_locks;
79+
`)
80+
iferr!=nil {
81+
returnnil,err
82+
}
83+
84+
deferrows.Close()
85+
86+
varlocks []PGLock
87+
err=sqlx.StructScan(rows,&locks)
88+
iferr!=nil {
89+
returnnil,err
90+
}
91+
92+
returnlocks,err
93+
}
94+
95+
typePGLocks []PGLock
96+
97+
func (lPGLocks)String()string {
98+
// Try to group things together by relation name.
99+
sort.Slice(l,func(i,jint)bool {
100+
returnsafeString(l[i].RelationName)<safeString(l[j].RelationName)
101+
})
102+
103+
varout strings.Builder
104+
fori,lock:=rangel {
105+
ifi!=0 {
106+
_,_=out.WriteString("\n")
107+
}
108+
_,_=out.WriteString(lock.String())
109+
}
110+
returnout.String()
111+
}
112+
113+
// Difference returns the difference between two sets of locks.
114+
// This is helpful to determine what changed between the two sets.
115+
func (lPGLocks)Difference(toPGLocks) (newPGLocks,removedPGLocks) {
116+
returnslice.SymmetricDifferenceFunc(l,to,func(a,bPGLock)bool {
117+
returna.Equal(b)
118+
})
119+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp