gorm

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 17, 2023 License: Apache-2.0, BSD-3-Clause, MIT, + 1 more Imports: 29 Imported by: 1

README

Gorm trpc plugin

English | 中文

This plugin provides a wrapper for the Gorm in trpc, allowing you to use the native Gorm interface while reusing trpc's plugin ecosystem.

Table of Contents

[TOC]

Background

During the development process of the middleware, developers often need to interact with databases. In order to leverage the capabilities of trpc-go, such as dynamic addressing and trace and monitoring, it is recommand to use the this gorm plugin.

Various ORM frameworks can effectively solve code quality and engineering efficiency issues. However, without trpc-go integration, it is not possible to use the native trpc-go framework configuration, filters, addressing, and other features. Therefore, we need to create an ORM plugin.

Currently, the most popular ORM framework in Go is Gorm, which has a high level of community activity and good acceptance among team developers.

Quick Start

Currently, it supports MySQL/ClickHouse and has made adjustments in the code to quickly iterate and support other types of databases.

tRPC-Go Framework Configuration

The plugin enforces setting the timeout to 0. This is because trpc-go cancels the context after receiving a request response, leading to competition with native database/sql for results and causing "Context Cancelled" errors. You can set the timeout directly in the address or use the context to implement timeout functionality.

...
client:                                     
  service:                                         
    - name: trpc.mysql.xxxx.xxxx # initialized as mysql
      target: dsn://root:xxxxxxg@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True
      # timeout setting is ineffective
    - name: trpc.mysql.xxxx.xxxx # initialized as mysql
      target: gorm+polaris://root:xxxxxxg@tcp(trpc.mysql.xxxx.xxxx)/test?charset=utf8mb4&parseTime=True
      namespace: Production
    - name: trpc.clickhouse.xxxx.xxx  # initialized as clickhouse
      target: dsn://tcp://localhost:9000/${database}?username=user&password=qwerty&read_timeout=10
Connection Pool Configuration Parameters (Optional)

You can configure connection pool parameters through the plugin's configuration.

plugins: # Plugin configuration
  database:
    gorm:
      # Default connection pool configuration for all database connections
      max_idle: 20 # Maximum number of idle connections
      max_open: 100 # Maximum number of open connections
      max_lifetime: 180000 # Maximum connection lifetime (in milliseconds)
      # Individual connection pool configuration for specific database connections
      service:
        - name: trpc.mysql.xxxx.xxxx
          max_idle: 10 
          max_open: 50 
          max_lifetime: 180000 
          driver_name: xxx # Driver used for the connection (empty by default, import the corresponding driver if specifying)

gorm Logger Configuration (Optional)

You can configure logging parameters using plugins. Here is an example of configuring logging parameters using YAML syntax:

plugins: # Plugin configuration
  database:
    gorm:
      # Default logging configuration for all database connections
      logger:
        slow_threshold: 1000 # Slow query threshold in milliseconds
        colorful: true # Whether to colorize the logs
        ignore_record_not_found_error: true # Whether to ignore errors when records are not found
        log_level: 4 # 1: Silent, 2: Error, 3: Warn, 4: Info
      # Individual logging configuration for specific database connections
      service:
        - name: trpc.mysql.xxxx.xxxx
          logger:
            slow_threshold: 1000 
            colorful: true 
            ignore_record_not_found_error: true 
            log_level: 4 

Code Implementation

The NewClientProxy method of this plugin can return a gorm.DB pointer that handles all requests through trpc. You can use this pointer to directly call native gorm methods.

Native gorm documentation: https://gorm.io/zh_CN/docs/index.html

If you encounter any issues, please open an issue in the project or contact the developers.

// Simple usage example, only supports MySQL. gormDB is the native gorm.DB pointer.
gormDB := gorm.NewClientProxy("trpc.mysql.test.test")
gormDB.Where("current_owners = ?", "xxxx").Where("id < ?", xxxx).Find(&owners)

If you need to make additional settings to the DB or use a database other than MySQL, you can use the native gorm.Open function and the ConnPool provided by this plugin.

import (
	"gorm.io/gorm"
	"gorm.io/gorm/mysql"
  gormplugin "trpc.group/trpc-go/trpc-database/gorm"
)

connPool := gormplugin.NewConnPool("trpc.mysql.test.test")
gormDB := gorm.Open(
	mysql.New(
		mysql.Config{
			Conn: connPool,
		}),
	&gorm.Config{
		Logger:  gormplugin.DefaultTRPCLogger, // For example: pass a custom logger
	},
)
Logging

Due to gorm's logging being output to stdout, it doesn't output to the tRPC-Go logs. This plugin wraps the tRPC log, allowing gorm logs to be printed in the tRPC log.

The gorm.DB generated by the NewClientProxy method is already equipped with the tRPC logger. If you open the ConnPool using gorm.Open, you can use the previously mentioned example to utilize the wrapped tRPC logger.

Example of logging:

gormDB := gorm.NewClientProxy("trpc.mysql.test.test")
gormDB.Debug().Where("current_owners = ?", "xxxx").Where("id < ?", xxxx).Find(&owners)
Context

When using the database plugin, you may need to report trace information and pass a context with the request. Gorm provides the WithContext method to include a context.

Example:

gormDB := gorm.NewClientProxy("trpc.mysql.test.test")
gormDB.WithContext(ctx).Where("current_owners = ?", "xxxx").Where("id < ?", xxxx).Find(&owners)
Unit Testing

You can use sqlmock to mock a sql.DB and open it with the native gorm.Open function to obtain a gorm.DB where you can control the results.

db, mock, _ := sqlmock.New()
defer db.Close()
gormDB, _ := gorm.Open(mysql.New(mysql.Config{Conn: db}))

To mock the returned results, you can use the following syntax:

mock.ExpectQuery(`SELECT * FROM "blogs"`).WillReturnRows(sqlmock.NewRows(nil))
mock.ExpectExec("INSERT INTO users").WithArgs("john", AnyTime{}).WillReturnResult(NewResult(1, 1))

For more details, you can refer to the documentation of sqlmock: https://github.com/DATA-DOG/go-sqlmock

If you are not sure about the specific SQL statements, you can temporarily set the log level to Info using the Debug() function to retrieve the SQL statements from the logs.

Example:

gormDB.Debug().Where("current_owners = ?", "xxxx").Where("id < ?", xxxx).Find(&owners)

Implementation Approach

gorm interacts with the database using the *sql.DB library. gorm also supports custom database connections, as mentioned in the official documentation:

GORM allows initializing *gorm.DB with an existing database connection

Specific Implementation

Package Structure
gorm
├── client.go      	# Entry point for using this plugin
├── codec.go      	# Encoding and decoding module
├── plugin.go			# Implementation of plugin configuration
└── transport.go		# Actual data sending and receiving

Main Logic Explanation

In the gorm framework, all interactions with the DB are done through DB.ConnPool, which is an interface. So, as long as our Client implements the ConnPool interface, gorm can use the Client to interact with the DB.

The definition of ConnPool can be found here: ConnPool

After practical usage, it was found that the implementation of ConnPool methods in gormCli alone is not enough. For example, it was not possible to interact with the database using transactions. Therefore, a comprehensive code search was performed to identify all the methods in gorm that call ConnPool, and they were implemented accordingly.

Thus, the Client becomes a custom connection that satisfies gorm's requirements, similar to sql.DB, but implementing only a subset of sql.DB functionality.

Additionally, there is a TxClient used for transaction handling, which implements both the ConnPool and TxCommitter interfaces defined by gorm.

FAQ

How to print specific SQL statements and results?

This plugin has implemented the TRPC Logger for GORM, as mentioned in the "Logging" section above. If you are using the default NewClientProxy, you can use the Debug() method before the request to output the SQL statements to the TRPC log with the Info level.

Example:

gormDB.Debug().Where("current_owners = ?", "xxxx").Where("id < ?", xxxx).Find(&owners)
How to include TraceID and full-chain timeout information in requests?

You can use the WithContext method to pass the context to GORM.

Example:

gormDB.WithContext(ctx).Where("current_owners = ?", "xxxx").Where("id < ?", xxxx).Find(&owners)
How to set the isolation level for transactions?

When starting a transaction, you can provide sql.TxOptions in the Begin method to set the isolation level. Alternatively, you can set it manually after starting the transaction using tx.Exec.

Currently, only MySQL, ClickHouse, and PostgreSQL are supported.
When using GORM transactions, only the Begin method goes through the tRPC call chain.

This is a design compromise made to reduce complexity. In this plugin, when calling BeginTx, it directly returns the result of sql.DB.BeginTx(), which is an already opened transaction. Subsequent transaction operations are handled by that transaction.

Considering that this plugin is mainly designed for connecting to MySQL instances, this approach can reduce some complexity while ensuring normal operation. However, considering that there may be services that require all database requests to go through the tRPC filter, the mechanism will be modified in the future to make all requests within the transaction go through the tRPC request flow.

If inaccurate reporting of data is caused by this behavior, you can disable the GORM transaction optimization to ensure that all requests that do not explicitly use transactions go through the basic reporting methods.

Example:

connPool := gormplugin.NewConnPool("trpc.mysql.test.test")
gormDB, err := gorm.Open(
	mysql.New(
		mysql.Config{
			Conn: connPool,
		}),
	&gorm.Config{
      Logger:  gormplugin.DefaultTRPCLogger, // Pass in a custom Logger
      SkipDefaultTransaction: true, // Disable GORM transaction optimization
	},
)

Documentation

Overview

Package gorm encapsulates the tRPC plugin in GORM.

Package gorm is a generated GoMock package.

Index

Constants

This section is empty.

Variables

View Source
var (
	DefaultClientCodec = &ClientCodec{}
)

default codec

View Source
var DefaultTRPCLogger = NewTRPCLogger(logger.Config{
	LogLevel:                  logger.Warn,
	IgnoreRecordNotFoundError: true,
})

DefaultTRPCLogger is the GORM logger that connects to trpc-log/log.

View Source
var NewClientProxy = func(name string, opts ...client.Option) (*gorm.DB, error) {
	connPool := NewConnPool(name, opts...)

	splitServiceName := strings.Split(name, ".")
	if len(splitServiceName) < 2 {
		return gorm.Open(
			mysql.New(
				mysql.Config{
					Conn: connPool,
				}),
			&gorm.Config{
				Logger: getLogger(name),
			})
	}

	dbEngineType := splitServiceName[1]

	switch dbEngineType {
	case "postgres":

		return gorm.Open(
			postgres.New(
				postgres.Config{
					PreferSimpleProtocol: true,
					Conn:                 connPool,
				}),
			&gorm.Config{
				Logger: getLogger(name),
			})
	default:
		return gorm.Open(
			mysql.New(
				mysql.Config{
					Conn: connPool,
				}),
			&gorm.Config{
				Logger: getLogger(name),
			},
		)
	}
}

NewClientProxy generates a gorm.DB that can connect to a specified location and send all requests through tRPC.

View Source
var NewConnPool = func(name string, opts ...client.Option) ConnPool {
	c := &Client{
		ServiceName: name,
		Client:      client.DefaultClient,
	}
	c.opts = make([]client.Option, 0, len(opts)+3)
	c.opts = append(c.opts, opts...)
	c.opts = append(c.opts,
		client.WithProtocol("gorm"),
		client.WithDisableServiceRouter(),
		client.WithTimeout(0),
	)
	return c
}

NewConnPool generates a ConnPool that sends all requests through tRPC. This ConnPool can be used as a parameter to generate gorm.DB, making it easier to adjust other configurations of GORM.

Functions

This section is empty.

Types

type Client

type Client struct {
	ServiceName string
	Client      client.Client
	// contains filtered or unexported fields
}

Client encapsulates the tRPC client and implements the ConnPoolBeginner interface.

func (*Client) BeginTx

func (gc *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (gorm.ConnPool, error)

BeginTx implements the ConnPoolBeginner.BeginTx method.

func (*Client) ExecContext

func (gc *Client) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)

ExecContext implements the ConnPool.ExecContext method.

func (*Client) GetDBConn

func (gc *Client) GetDBConn() (*sql.DB, error)

GetDBConn implements the GetDBConn method.

func (*Client) Ping

func (gc *Client) Ping() error

Ping implements the Ping method.

func (*Client) PrepareContext

func (gc *Client) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)

PrepareContext implements the ConnPool.PrepareContext method.

func (*Client) QueryContext

func (gc *Client) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)

QueryContext implements the ConnPool.QueryContext method.

func (*Client) QueryRowContext

func (gc *Client) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row

QueryRowContext implements the ConnPool.QueryRowContext method.

type ClientCodec

type ClientCodec struct{}

ClientCodec decodes db client requests.

func (*ClientCodec) Decode

func (c *ClientCodec) Decode(msg codec.Msg, buffer []byte) (body []byte, err error)

Decode parses the metadata in the response from the db client.

func (*ClientCodec) Encode

func (c *ClientCodec) Encode(msg codec.Msg, body []byte) (buffer []byte, err error)

Encode sets the metadata of the db client request.

type ClientTransport

type ClientTransport struct {
	SQLDB             map[string]*sql.DB
	SQLDBLock         sync.RWMutex
	DefaultPoolConfig PoolConfig
	PoolConfigs       map[string]PoolConfig
	// contains filtered or unexported fields
}

ClientTransport is a struct that implements the trpc ClientTransport interface and is responsible for sending requests.

func NewClientTransport

func NewClientTransport(opt ...transport.ClientTransportOption) *ClientTransport

NewClientTransport creates transport.

func (*ClientTransport) GetDB

func (ct *ClientTransport) GetDB(serviceName, dsn string) (*sql.DB, error)

GetDB retrieves the database connection, currently supports mysql/clickhouse, can be extended for other types of databases.

func (*ClientTransport) RoundTrip

func (ct *ClientTransport) RoundTrip(ctx context.Context, reqBuf []byte,
	callOpts ...transport.RoundTripOption) (rspBuf []byte, err error)

RoundTrip sends a SQL request and handles the SQL response.

type Config

type Config struct {
	MaxIdle     int           `yaml:"max_idle"`     // Maximum number of idle connections.
	MaxOpen     int           `yaml:"max_open"`     // Maximum number of connections that can be open at same time.
	MaxLifetime int           `yaml:"max_lifetime"` // The maximum lifetime of each connection, in milliseconds.
	Logger      *loggerConfig `yaml:"logger"`       // Logger configuration.
	Service     []struct {
		// In the case of having multiple database connections,
		// you can configure the connection pool independently.
		Name        string
		MaxIdle     int `yaml:"max_idle"`     // Maximum number of idle connections.
		MaxOpen     int `yaml:"max_open"`     // Maximum number of connections that can be open at same time.
		MaxLifetime int `yaml:"max_lifetime"` // The maximum lifetime of each connection, in milliseconds.
		// The name of the custom driver used, which is empty by default.
		DriverName string        `yaml:"driver_name"`
		Logger     *loggerConfig `yaml:"logger"` // Logger configuration.
	}
}

Config is the struct for the configuration of the SQL proxy.

type ConnPool

type ConnPool interface {
	PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
	ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
	QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
	// BeginTx implements ConnPoolBeginner.
	BeginTx(ctx context.Context, opts *sql.TxOptions) (gorm.ConnPool, error)
	// Ping implements pinger.
	Ping() error
	// GetDBConn implements GetDBConnector.
	GetDBConn() (*sql.DB, error)
}

ConnPool implements the gorm.ConnPool interface as well as transaction and Ping functionality.

type MockLogger

type MockLogger struct {
	// contains filtered or unexported fields
}

MockLogger is a mock of Logger interface.

func NewMockLogger

func NewMockLogger(ctrl *gomock.Controller) *MockLogger

NewMockLogger creates a new mock instance.

func (*MockLogger) Debug

func (m *MockLogger) Debug(arg0 ...interface{})

Debug mocks base method.

func (*MockLogger) Debugf

func (m *MockLogger) Debugf(arg0 string, arg1 ...interface{})

Debugf mocks base method.

func (*MockLogger) EXPECT

func (m *MockLogger) EXPECT() *MockLoggerMockRecorder

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockLogger) Error

func (m *MockLogger) Error(arg0 ...interface{})

Error mocks base method.

func (*MockLogger) Errorf

func (m *MockLogger) Errorf(arg0 string, arg1 ...interface{})

Errorf mocks base method.

func (*MockLogger) Fatal

func (m *MockLogger) Fatal(arg0 ...interface{})

Fatal mocks base method.

func (*MockLogger) Fatalf

func (m *MockLogger) Fatalf(arg0 string, arg1 ...interface{})

Fatalf mocks base method.

func (*MockLogger) GetLevel

func (m *MockLogger) GetLevel(arg0 string) log.Level

GetLevel mocks base method.

func (*MockLogger) Info

func (m *MockLogger) Info(arg0 ...interface{})

Info mocks base method.

func (*MockLogger) Infof

func (m *MockLogger) Infof(arg0 string, arg1 ...interface{})

Infof mocks base method.

func (*MockLogger) SetLevel

func (m *MockLogger) SetLevel(arg0 string, arg1 log.Level)

SetLevel mocks base method.

func (*MockLogger) Sync

func (m *MockLogger) Sync() error

Sync mocks base method.

func (*MockLogger) Trace

func (m *MockLogger) Trace(arg0 ...interface{})

Trace mocks base method.

func (*MockLogger) Tracef

func (m *MockLogger) Tracef(arg0 string, arg1 ...interface{})

Tracef mocks base method.

func (*MockLogger) Warn

func (m *MockLogger) Warn(arg0 ...interface{})

Warn mocks base method.

func (*MockLogger) Warnf

func (m *MockLogger) Warnf(arg0 string, arg1 ...interface{})

Warnf mocks base method.

func (*MockLogger) With

func (m *MockLogger) With(arg0 ...log.Field) log.Logger

With mocks base method.

func (*MockLogger) WithFields

func (m *MockLogger) WithFields(arg0 ...string) log.Logger

WithFields mocks base method.

type MockLoggerMockRecorder

type MockLoggerMockRecorder struct {
	// contains filtered or unexported fields
}

MockLoggerMockRecorder is the mock recorder for MockLogger.

func (*MockLoggerMockRecorder) Debug

func (mr *MockLoggerMockRecorder) Debug(arg0 ...interface{}) *gomock.Call

Debug indicates an expected call of Debug.

func (*MockLoggerMockRecorder) Debugf

func (mr *MockLoggerMockRecorder) Debugf(arg0 interface{}, arg1 ...interface{}) *gomock.Call

Debugf indicates an expected call of Debugf.

func (*MockLoggerMockRecorder) Error

func (mr *MockLoggerMockRecorder) Error(arg0 ...interface{}) *gomock.Call

Error indicates an expected call of Error.

func (*MockLoggerMockRecorder) Errorf

func (mr *MockLoggerMockRecorder) Errorf(arg0 interface{}, arg1 ...interface{}) *gomock.Call

Errorf indicates an expected call of Errorf.

func (*MockLoggerMockRecorder) Fatal

func (mr *MockLoggerMockRecorder) Fatal(arg0 ...interface{}) *gomock.Call

Fatal indicates an expected call of Fatal.

func (*MockLoggerMockRecorder) Fatalf

func (mr *MockLoggerMockRecorder) Fatalf(arg0 interface{}, arg1 ...interface{}) *gomock.Call

Fatalf indicates an expected call of Fatalf.

func (*MockLoggerMockRecorder) GetLevel

func (mr *MockLoggerMockRecorder) GetLevel(arg0 interface{}) *gomock.Call

GetLevel indicates an expected call of GetLevel.

func (*MockLoggerMockRecorder) Info

func (mr *MockLoggerMockRecorder) Info(arg0 ...interface{}) *gomock.Call

Info indicates an expected call of Info.

func (*MockLoggerMockRecorder) Infof

func (mr *MockLoggerMockRecorder) Infof(arg0 interface{}, arg1 ...interface{}) *gomock.Call

Infof indicates an expected call of Infof.

func (*MockLoggerMockRecorder) SetLevel

func (mr *MockLoggerMockRecorder) SetLevel(arg0, arg1 interface{}) *gomock.Call

SetLevel indicates an expected call of SetLevel.

func (*MockLoggerMockRecorder) Sync

func (mr *MockLoggerMockRecorder) Sync() *gomock.Call

Sync indicates an expected call of Sync.

func (*MockLoggerMockRecorder) Trace

func (mr *MockLoggerMockRecorder) Trace(arg0 ...interface{}) *gomock.Call

Trace indicates an expected call of Trace.

func (*MockLoggerMockRecorder) Tracef

func (mr *MockLoggerMockRecorder) Tracef(arg0 interface{}, arg1 ...interface{}) *gomock.Call

Tracef indicates an expected call of Tracef.

func (*MockLoggerMockRecorder) Warn

func (mr *MockLoggerMockRecorder) Warn(arg0 ...interface{}) *gomock.Call

Warn indicates an expected call of Warn.

func (*MockLoggerMockRecorder) Warnf

func (mr *MockLoggerMockRecorder) Warnf(arg0 interface{}, arg1 ...interface{}) *gomock.Call

Warnf indicates an expected call of Warnf.

func (*MockLoggerMockRecorder) With

func (mr *MockLoggerMockRecorder) With(arg0 ...interface{}) *gomock.Call

With indicates an expected call of With.

func (*MockLoggerMockRecorder) WithFields

func (mr *MockLoggerMockRecorder) WithFields(arg0 ...interface{}) *gomock.Call

WithFields indicates an expected call of WithFields.

type OpEnum

type OpEnum int

OpEnum is the database operation enumeration type.

const (
	OpPrepareContext OpEnum = iota + 1
	OpExecContext
	OpQueryContext
	OpQueryRowContext
	OpBeginTx
	OpPing
	OpCommit
	OpRollback
	OpGetDB
)

Constants for database operation types. Starting from 1 to avoid using the default value 0.

func (OpEnum) String

func (op OpEnum) String() string

String converts the operation type constant to human-readable text.

type Plugin

type Plugin struct{}

Plugin used to load the configuration of sql.DB connection parameters.

func (*Plugin) Setup

func (m *Plugin) Setup(name string, configDesc plugin.Decoder) (err error)

Setup initializes the plugin. If the plugin has been specially configured in trpc_go.yaml, use the configuration to regenerate ClientTransport and register it into the tRPC framework to replace the default ClientTransport. This function will be executed in trpc.NewServer().

func (*Plugin) Type

func (m *Plugin) Type() string

Type returns the type of the plugin.

type PoolConfig

type PoolConfig struct {
	MaxIdle     int
	MaxOpen     int
	MaxLifetime time.Duration
	DriverName  string
}

PoolConfig is the configuration of the database connection pool.

type Request

type Request struct {
	// Op is the type of database operation performed.
	Op        OpEnum
	Query     string
	Args      []interface{}
	Tx        *sql.Tx
	TxOptions *sql.TxOptions
}

Request is the request passed to the tRPC framework.

type Response

type Response struct {
	Result sql.Result
	Stmt   *sql.Stmt
	Row    *sql.Row
	Rows   *sql.Rows
	Tx     *sql.Tx
	DB     *sql.DB
}

Response is the result returned by the tRPC framework.

type TRPCLogger

type TRPCLogger struct {
	// contains filtered or unexported fields
}

TRPCLogger implements the Gorm logger.Interface.

func NewTRPCLogger

func NewTRPCLogger(config logger.Config) *TRPCLogger

NewTRPCLogger creates a TRPCLogger based on the configuration parameters.

func (*TRPCLogger) Error

func (p *TRPCLogger) Error(ctx context.Context, format string, args ...interface{})

Error prints logs at the Error level.

func (*TRPCLogger) Info

func (p *TRPCLogger) Info(ctx context.Context, format string, args ...interface{})

Info prints logs at the Info level.

func (*TRPCLogger) LogMode

func (p *TRPCLogger) LogMode(level logger.LogLevel) logger.Interface

LogMode changes the log level and returns a new TRPCLogger.

func (*TRPCLogger) Trace

func (p *TRPCLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error)

Trace prints detailed SQL logs of the corresponding level based on the SQL execution situation and the configured log level.

func (*TRPCLogger) Warn

func (p *TRPCLogger) Warn(ctx context.Context, format string, args ...interface{})

Warn prints logs at the Warn level.

type TxClient

type TxClient struct {
	Client *Client
	Tx     *sql.Tx
}

TxClient is the TRPC client with a transaction opened, and it implements the TxCommitter interface. The reason for separating the implementation of Client and TxClient

	is that GORM defines two interfaces to support transaction operations:
 1. ConnPoolBeginner: including the method for opening transactions using BeginTx,
    corresponding to the Client object of this plugin.
 2. TxCommitter: including the methods for committing and rolling back transactions using Commit and Rollback,
    corresponding to the TxClient object of this plugin.

These two interfaces correspond to two types of connection pools,

before and after opening transactions, which cannot be mixed.

For example, under the GORM automatic transaction mechanism (SkipDefaultTransaction=false),

for operations such as Create and Delete,
if the connection pool has implemented the 'ConnPoolBeginner' interface, it will automatically open a transaction.
If the current connection itself has already opened a transaction,
it will cause an exception of 'lock wait timeout exceeded' due to the duplicate opening of the transaction,
as detailed in the GORM.DB.Begin method.

Therefore, after opening a transaction,

you need to convert the Client object to a connection object
that has not implemented the 'ConnPoolBeginner' interface: TxClient.

The calling logic is:

  1. Use Client before opening a transaction. At this time, the Client implements the ConnPoolBeginner interface and can call BeginTx to open a transaction.
  2. Call Client.BeginTx to open a transaction and return TxClient.

func (*TxClient) Commit

func (txgc *TxClient) Commit() error

Commit performs a commit.

func (*TxClient) ExecContext

func (txgc *TxClient) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)

ExecContext implements the ConnPool.ExecContext method.

func (*TxClient) PrepareContext

func (txgc *TxClient) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)

PrepareContext implements the ConnPool.PrepareContext method.

func (*TxClient) QueryContext

func (txgc *TxClient) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)

QueryContext implements the ConnPool.QueryContext method.

func (*TxClient) QueryRowContext

func (txgc *TxClient) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row

QueryRowContext implements the ConnPool.QueryRowContext method.

func (*TxClient) Rollback

func (txgc *TxClient) Rollback() error

Rollback rolls back the transaction.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL