GORM+MySQL Connection Pool

GORM 의 Connection pool

νšŒμ‚¬μ—μ„œ 마주친 μ—λŸ¬

$ [mysql] 2024/03/11 18:37:09 packets.go:122: closing bad idle connection: unexpected read from socket
$ [mysql] 2024/03/11 18:37:09 packets.go:122: closing bad idle connection: unexpected read from socket

Batch 처리λ₯Ό μš”ν•˜λŠ” μž‘μ—…λ“€μ΄ λͺ¨μ—¬μžˆλŠ” μΈμŠ€ν„΄μŠ€μ—μ„œ λ°œμƒν–ˆλ‹€.

λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μžμ²΄μ—λŠ” λ¬Έμ œκ°€ μ—†μ—ˆκ³ , 컀λ„₯μ…˜ κ΄€λ ¨ 섀정도 λ””ν΄νŠΈλ‘œ λ˜μ–΄μžˆλŠ” μƒν™©μ΄μ—ˆλ‹€.

사싀 μ„œλΉ„μŠ€ μ „μ²΄μ μœΌλ‘œ 보면 MySQL 자체λ₯Ό 많이 μ“°λŠ” 상황은 μ•„λ‹ˆμ—ˆκ³ , λ™μ‹œμ— μ‹€ν–‰λ˜λŠ” job 이 MySQL κΈ°λ°˜μ΄μ—ˆμ–΄μ„œ 컀λ„₯μ…˜ κ΄€λ ¨ 문제둜 μ’ν˜€λ†“κ³  디버깅을 μ‹œμž‘ν–ˆλ‹€.

이 κ³Όμ •μ—μ„œ μƒˆλ‘­κ²Œ μ•Œκ²Œλœ 사싀에 λŒ€ν•œ 기둝.

MaxLifeTime

gorm 은 sql νŒ¨ν‚€μ§€μ˜ DB 객체λ₯Ό wrapping ν•˜κ³  μžˆλ‹€.

sql.DB κ΅¬μ‘°μ²΄μ—λŠ” Stats λΌλŠ” λ©”μ†Œλ“œκ°€ μžˆλ‹€.

var db := &sql.DB{}
		
stats := db.Stats() // stats ?

Stats κ΅¬μ‘°μ²΄λŠ” μ•„λž˜μ™€ 같이 생겼닀.

// DBStats contains database statistics.
type DBStats struct {
	MaxOpenConnections int // Maximum number of open connections to the database.

	// Pool Status
	OpenConnections int // The number of established connections both in use and idle.
	InUse           int // The number of connections currently in use.
	Idle            int // The number of idle connections.

	// Counters
	WaitCount         int64         // The total number of connections waited for.
	WaitDuration      time.Duration // The total time blocked waiting for a new connection.
	MaxIdleClosed     int64         // The total number of connections closed due to SetMaxIdleConns.
	MaxIdleTimeClosed int64         // The total number of connections closed due to SetConnMaxIdleTime.
	MaxLifetimeClosed int64         // The total number of connections closed due to SetConnMaxLifetime.
}

μœ„μ—μ„œ λΆ€ν„° μ½μ–΄λ³΄μž.

  • μ΅œλŒ€ 컀λ„₯μ…˜ 개수

  • ν˜„μž¬ μ—΄λ €μžˆλŠ” 컀λ„₯μ…˜ 개수

  • μ‚¬μš©μ€‘μΈ 컀λ„₯μ…˜ 개수

  • 유휴 컀λ„₯μ…˜ 개수

  • 컀λ„₯μ…˜μ„ λŒ€κΈ°ν•˜λŠ” 개수

  • 총 λŒ€κΈ° μ‹œκ°„

  • SetMaxIdleConns 둜 인해 λ‹«νžŒ 유휴 컀λ„₯μ…˜ 개수

  • SetConnMaxIdleTime 둜 인해 λ‹«νžŒ 유휴 컀λ„₯μ…˜ 개수

  • SetConnMaxLifetime 둜 인해 λ‹«νžŒ 컀λ„₯μ…˜ 개수

go 의 *sql.DB κ΅¬μ‘°μ²΄μ—μ„œλŠ” μ•„λž˜μ™€ 같이 섀정듀을 μ»€μŠ€ν„°λ§ˆμ΄μ§• ν•  수 μžˆλ‹€.

참고둜 기본값은 0으둜 λ“€μ–΄κ°€λŠ”λ°, 이 경우 μ œν•œμ΄ μ—†λ‹€λŠ” 말이닀.

λ¬Όλ‘  μ œν•œμ΄ μ—†λ‹€κ³ ν•΄μ„œ 컀λ„₯μ…˜μ„ λ¬΄ν•œλŒ€λ‘œ λ§Œλ“€μ–΄λ‚Όμˆ˜λŠ” μ—†λ‹€. λ˜ν•œ μ–΄μ§€κ°„ν•œ κ²½μš°μ—λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ λ¨Όμ € κ²€ν† ν•΄λ³΄λŠ” 것이 μ’‹λ‹€.

GORM μ—μ„œλŠ” μ•„λž˜μ™€ 같이 μ»€μŠ€ν„°λ§ˆμ΄μ§• ν•  것을 κΆŒκ³ ν•˜κ³  μžˆλ‹€.

μœ„μ—μ„œ μ–ΈκΈ‰ν–ˆλ“―μ΄, 컀λ„₯μ…˜ κ΄€λ ¨ 문제둜 보고 λͺ¨λ‹ˆν„°λ§μ„ μ‹œμž‘ν–ˆλ‹€.

주기적으둜 Stats λ₯Ό μ°μ–΄λ΄€μ„λ•Œ MaxConn 이 λ§Žμ•„λ„ 20을 λ„˜μ–΄κ°€μ§€ μ•Šμ•˜κ³ , νŠΉμ΄μ‚¬ν•­μœΌλ‘œ λ³΄κΈ°μ—λŠ” μ–΄λ €μ› λ‹€.

λ‹€λ§Œ νŠΉμ • μž‘μ—…μ΄ μ‹€ν–‰λ λ•Œλ§ˆλ‹€ OpenConn 이 λŠ˜μ–΄λ‚˜κ³  쀄어듀지 μ•ŠλŠ” λ¬Έμ œκ°€ λ°œκ²¬λ˜μ—ˆλ‹€.

μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™μ•˜λ‹€.

νŠΈλžœμž­μ…˜ μ•ˆμ—μ„œ νŠΈλžœμž­μ…˜ λŒ€μ‹  λ‹€λ₯Έ 컀λ„₯μ…˜μ„ κ³„μ†ν•΄μ„œ μ§‘μ–΄μ˜€λŠ” λ ˆκ±°μ‹œκ°€ μžˆμ—ˆλ‹€.

μ΄λŸ¬ν•œ Job 이 μ‹€ν–‰λ˜κ³  λ‚˜λ©΄ 컀λ„₯μ…˜ κ°œμˆ˜κ°€ ν•˜λ‚˜ λŠ˜μ–΄λ‚˜λŠ”λ°, 이런 μž‘μ—…μ΄ λͺ‡ κ°œκ°€ μžˆλ‹€λ³΄λ‹ˆ μΌμ’…μ˜ 컀λ„₯μ…˜ λˆ„μˆ˜κ°€ λ°œμƒν•œ κ²ƒμœΌλ‘œ μΆ”μ •ν–ˆλ‹€.

νŠΉμ΄ν•œ 것은 κ°•μ œλ‘œ μž¬μ‹€ν–‰μ„ ν–ˆμ„λ•Œ SavePoint λ‘œκ·Έκ°€ μ°νžˆλŠ” 것도 μ•„λ‹ˆκ³ , μ—λŸ¬κ°€ 찍힌 뒀에 싀행이 λ˜μ§€ μ•ŠλŠ” κ²½μš°μ™€ 싀행이 λ˜λŠ” 경우 λ“± λΆˆκ·œμΉ™μ μΈ ν–‰νƒœλ₯Ό λ³΄μΈλ‹€λŠ” 것이닀.

μš°μ„  ν•΄λ‹Ή λ ˆκ±°μ‹œλ₯Ό λͺ¨λ‘ μˆ˜μ •ν•˜κ³ , MaxConnLifetime 을 ν•œ μ‹œκ°„μœΌλ‘œ μ„€μ •ν•΄μ„œ κ³„μ†ν•΄μ„œ μ’€λΉ„ 컀λ„₯μ…˜μ΄ κ³„μ†ν•΄μ„œ μ‚΄μ•„λ‚¨λŠ” 것을 λ°©μ§€ν–ˆλ‹€.

아직 μž‘μ—…μ€ λͺ»ν–ˆμ§€λ§Œ μΆ”ν›„ 컀λ„₯μ…˜ ν’€μ—μ„œ ν•˜λ‚˜μ”© ν΄λ§ν•΄μ„œ λ°›μ•„μ˜€λŠ” ꡬ쑰둜 변경을 고렀쀑이닀.

참고 링크

https://github.com/go-sql-driver/mysql/issues/1120 https://gorm.io/docs/generic_interface.html

Last updated