Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill go-database명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | go-database |
| description | > Use when this capability is needed. |
Database access is where most Go services spend their complexity budget. Get connection management, transactions, and query patterns right.
func OpenDB(dsn string) (*sql.DB, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, fmt.Errorf("open db: %w", err)
}
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(1 * time.Minute)
if err := db.PingContext(context.Background()); err != nil {
return nil, fmt.Errorf("ping db: %w", err)
}
return db, nil
}
| Setting | Guideline |
|---|---|
MaxOpenConns | Match your DB's max connections / number of app instances |
MaxIdleConns | 40-50% of MaxOpenConns |
ConnMaxLifetime | 5-10 minutes (prevents stale connections behind load balancers) |
ConnMaxIdleTime | 1-2 minutes |
// ❌ Bad — unlimited connections (default)
db, _ := sql.Open("postgres", dsn)
// No pool config → unbounded connections → DB overload under load
// ✅ Good — context propagated
row := db.QueryRowContext(ctx, "SELECT id, name FROM users WHERE id = $1", id)
// ❌ Bad — no context, no cancellation support
row := db.QueryRow("SELECT id, name FROM users WHERE id = $1", id)
// ✅ Good — parameterized
rows, err := db.QueryContext(ctx,
"SELECT id, name FROM users WHERE status = $1 AND created_at > $2",
status, since,
)
// ❌ Bad — SQL injection vulnerability
rows, err := db.QueryContext(ctx,
fmt.Sprintf("SELECT id, name FROM users WHERE status = '%s'", status),
)
rows, err := db.QueryContext(ctx, query, args...)
if err != nil {
return fmt.Errorf("query users: %w", err)
}
defer rows.Close()
var users []User
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Name, &u.Email); err != nil {
return fmt.Errorf("scan user: %w", err)
}
users = append(users, u)
}
// ALWAYS check rows.Err() after iteration
if err := rows.Err(); err != nil {
return fmt.Errorf("iterate users: %w", err)
}
var user User
err := db.QueryRowContext(ctx,
"SELECT id, name, email FROM users WHERE id = $1", id,
).Scan(&user.ID, &user.Name, &user.Email)
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrUserNotFound
}
if err != nil {
return nil, fmt.Errorf("get user %s: %w", id, err)
}
func WithTx(ctx context.Context, db *sql.DB, fn func(tx *sql.Tx) error) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("begin tx: %w", err)
}
if err := fn(tx); err != nil {
if rbErr := tx.Rollback(); rbErr != nil {
return fmt.Errorf("rollback failed: %v (original: %w)", rbErr, err)
}
return err
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("commit tx: %w", err)
}
return nil
}
err := WithTx(ctx, db, func(tx *sql.Tx) error {
if _, err := tx.ExecContext(ctx,
"UPDATE accounts SET balance = balance - $1 WHERE id = $2", amount, fromID,
); err != nil {
return fmt.Errorf("debit: %w", err)
}
if _, err := tx.ExecContext(ctx,
"UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, toID,
); err != nil {
return fmt.Errorf("credit: %w", err)
}
return nil
})
tx, err := db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelSerializable, // for critical financial operations
})
type UserRepository interface {
GetByID(ctx context.Context, id string) (*User, error)
List(ctx context.Context, filter UserFilter) ([]User, error)
Create(ctx context.Context, user *User) error
Update(ctx context.Context, user *User) error
Delete(ctx context.Context, id string) error
}
type pgUserRepo struct {
db *sql.DB
}
func NewUserRepository(db *sql.DB) UserRepository {
return &pgUserRepo{db: db}
}
func (r *pgUserRepo) GetByID(ctx context.Context, id string) (*User, error) {
var u User
err := r.db.QueryRowContext(ctx,
"SELECT id, name, email, created_at FROM users WHERE id = $1", id,
).Scan(&u.ID, &u.Name, &u.Email, &u.CreatedAt)
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrUserNotFound
}
if err != nil {
return nil, fmt.Errorf("get user %s: %w", id, err)
}
return &u, nil
}
Prefer sqlc for projects that use raw SQL. It generates type-safe Go code from SQL queries.
-- name: GetUser :one
SELECT id, name, email, created_at
FROM users
WHERE id = $1;
-- name: ListUsers :many
SELECT id, name, email, created_at
FROM users
WHERE status = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3;
-- name: CreateUser :one
INSERT INTO users (name, email)
VALUES ($1, $2)
RETURNING id, name, email, created_at;
sqlc generates Go code with proper types, eliminating manual Scan calls
and catching query/schema mismatches at build time.
Recommended tools: goose, golang-migrate, atlas.
up and down (rollback) SQL-- +goose Up
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
-- +goose Down
ALTER TABLE users DROP COLUMN phone;
// ✅ Good — separate migration command
// cmd/migrate/main.go runs migrations
// cmd/server/main.go starts the server
// ❌ Bad — migrations in server startup
func main() {
runMigrations(db) // blocks startup, risky in multi-instance deploys
startServer()
}
// ✅ Good — use sql.Null types or pointers
type User struct {
ID string
Name string
Phone sql.NullString // nullable column
}
// Or with pointers:
type User struct {
ID string
Name string
Phone *string // nil = SQL NULL
}
// ❌ Bad — N+1 query pattern
users, _ := listUsers(ctx)
for _, u := range users {
orders, _ := getOrdersByUser(ctx, u.ID) // 1 query per user
u.Orders = orders
}
// ✅ Good — single query with JOIN or batch
users, _ := listUsersWithOrders(ctx) // JOIN or subquery
// ❌ Bad — rows not closed on early return
rows, err := db.QueryContext(ctx, query)
if err != nil {
return err
}
// forgot defer rows.Close()
if someCondition {
return nil // rows leaked!
}
MaxOpenConns, MaxIdleConns, lifetimes)QueryContext results have defer rows.Close() immediately after error checkrows.Err() checked after row iteration loopsql.ErrNoRows handled explicitly with errors.Is*Context variants)sql.NullString / sql.NullInt64 or pointer typesSource: eduardo-sl/go-agent-skills — distributed by TomeVault.