Skip to content

Commit d7f6378

Browse files
authored
Merge gorm to tag mongodb/v0.2.1 (#39)
1 parent 48a4003 commit d7f6378

32 files changed

+2230
-324
lines changed

gorm/README.md

Lines changed: 212 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,66 @@ Currently, the most popular ORM framework in Go is Gorm, which has a high level
2323

2424
## Quick Start
2525

26-
Currently, it supports MySQL/ClickHouse and has made adjustments in the code to quickly iterate and support other types of databases.
26+
Add trpc_go.yaml framework configuration:
27+
28+
```yaml
29+
client:
30+
service:
31+
- name: trpc.mysql.server.service
32+
# Reference: https://github.com/go-sql-driver/mysql?tab=readme-ov-file#dsn-data-source-name
33+
target: dsn://root:123456@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True
34+
```
35+
36+
Code implementation:
37+
38+
```go
39+
package main
40+
41+
import (
42+
"github.com/trpc-group/trpc-database/gorm"
43+
"trpc.group/trpc-go/trpc-go"
44+
"trpc.group/trpc-go/trpc-go/log"
45+
)
46+
47+
type User struct {
48+
ID int
49+
Username string
50+
}
51+
52+
func main() {
53+
_ = trpc.NewServer()
54+
55+
cli, err := gorm.NewClientProxy("trpc.mysql.server.service")
56+
if err != nil {
57+
panic(err)
58+
}
59+
60+
// Create record
61+
insertUser := User{Username: "gorm-client"}
62+
result := cli.Create(&insertUser)
63+
log.Infof("inserted data's primary key: %d, err: %v", insertUser.ID, result.Error)
64+
65+
// Query record
66+
var queryUser User
67+
if err := cli.First(&queryUser).Error; err != nil {
68+
panic(err)
69+
}
70+
log.Infof("query user: %+v", queryUser)
71+
72+
// Delete record
73+
deleteUser := User{ID: insertUser.ID}
74+
if err := cli.Delete(&deleteUser).Error; err != nil {
75+
panic(err)
76+
}
77+
log.Info("delete record succeed")
78+
79+
// For more use cases, see https://gorm.io/docs/create.html
80+
}
81+
```
82+
83+
Complete example: [mysql-example](examples/mysql/main.go)
84+
85+
## Complete Configuration
2786

2887
### tRPC-Go Framework Configuration
2988

@@ -52,43 +111,109 @@ plugins: # Plugin configuration
52111
database:
53112
gorm:
54113
# Default connection pool configuration for all database connections
55-
max_idle: 20 # Maximum number of idle connections
56-
max_open: 100 # Maximum number of open connections
57-
max_lifetime: 180000 # Maximum connection lifetime (in milliseconds)
114+
max_idle: 20 # Maximum number of idle connections (default 10 if not set or set to 0); if negative, no idle connections are retained
115+
max_open: 100 # Maximum number of open connections (default 10000 if not set or set to 0); if negative, no limit on open connections
116+
max_lifetime: 180000 # Maximum connection lifetime in milliseconds (default 3min); if negative, connections are not closed due to age
117+
driver_name: mysql # Driver used for the connection (empty by default, import the corresponding driver if specifying)
118+
logger: # this feature is supported in versions >= v0.2.2
119+
slow_threshold: 200 # Slow query threshold in milliseconds, 0 means no slow query logging (default 0)
120+
colorful: false # Whether to colorize the logs (default false)
121+
ignore_record_not_found_error: false # Whether to ignore errors when records are not found (default false)
122+
log_level: 4 # Log level: 1:Silent, 2:Error, 3:Warn, 4:Info (default no logging)
123+
max_sql_size: 100 # Maximum SQL statement length for truncation, 0 means no limit (default 0)
58124
# Individual connection pool configuration for specific database connections
59125
service:
60-
- name: trpc.mysql.xxxx.xxxx
126+
- name: trpc.mysql.server.service
61127
max_idle: 10
62128
max_open: 50
63129
max_lifetime: 180000
64-
driver_name: xxx # Driver used for the connection (empty by default, import the corresponding driver if specifying)
130+
driver_name: mysql # Driver used for the connection (empty by default, import the corresponding driver if specifying)
131+
logger:
132+
slow_threshold: 1000
133+
colorful: true
134+
ignore_record_not_found_error: true
135+
log_level: 4
136+
```
137+
138+
## Advanced Features
139+
140+
### Polaris Routing and Addressing
65141
142+
If you need to use Polaris routing and addressing, first add:
143+
144+
`import _ "github.com/trpc-group/trpc-naming-polaris"`
145+
146+
Then use the `gorm+polaris` scheme in the framework configuration target, and write the Polaris service name in the original `host:port` position.
147+
148+
```yaml
149+
client:
150+
service:
151+
- name: trpc.mysql.xxxx.xxxx
152+
namespace: Production
153+
# Use gorm+polaris scheme, write Polaris service name in original host:port position
154+
target: gorm+polaris://root:123456@tcp(${polaris_name})/mydb?parseTime=True
66155
```
67156

68-
### gorm Logger Configuration (Optional)
157+
### ClickHouse Integration
69158

70-
You can configure logging parameters using plugins. Here is an example of configuring logging parameters using YAML syntax:
159+
Add trpc_go.yaml framework configuration:
71160

161+
Note that service.name uses the four-segment format `trpc.${app}.${server}.${service}`, where `${app}` should be `clickhouse`.
72162

73163
```yaml
74-
plugins: # Plugin configuration
164+
client:
165+
service:
166+
- name: trpc.clickhouse.server.service
167+
# Reference: https://github.com/ClickHouse/clickhouse-go?tab=readme-ov-file#dsn
168+
target: dsn://clickhouse://default:@127.0.0.1:9000/mydb?dial_timeout=200ms&max_execution_time=60
169+
# In gorm/v0.2.2 and earlier, using this DSN format would prompt username or password errors,
170+
# you should use the pre-change DSN format:
171+
# target: dsn://tcp://localhost:9000?username=user&password=qwerty&database=clicks&read_timeout=10&write_timeout=20
172+
```
173+
174+
Complete example: [clickhouse-example](examples/clickhouse/main.go)
175+
176+
### SQLite Integration
177+
178+
Add trpc_go.yaml framework configuration:
179+
180+
Note that service.name uses the four-segment format `trpc.${app}.${server}.${service}`, where `${app}` should be `sqlite`.
181+
182+
```yaml
183+
client:
184+
service:
185+
- name: trpc.sqlite.server.service
186+
# Reference: https://github.com/mattn/go-sqlite3?tab=readme-ov-file#dsn-examples
187+
# Execute "sqlite3 mysqlite.db" in the current directory to create the database
188+
target: dsn://file:mysqlite.db
189+
```
190+
191+
Add plugin configuration, keep the service name consistent, and configure `driver_name: sqlite3`.
192+
193+
```yaml
194+
plugins:
75195
database:
76196
gorm:
77-
# Default logging configuration for all database connections
78-
logger:
79-
slow_threshold: 1000 # Slow query threshold in milliseconds
80-
colorful: true # Whether to colorize the logs
81-
ignore_record_not_found_error: true # Whether to ignore errors when records are not found
82-
log_level: 4 # 1: Silent, 2: Error, 3: Warn, 4: Info
83-
# Individual logging configuration for specific database connections
84197
service:
85-
- name: trpc.mysql.xxxx.xxxx
86-
logger:
87-
slow_threshold: 1000
88-
colorful: true
89-
ignore_record_not_found_error: true
90-
log_level: 4
198+
# Configuration effective for trpc.sqlite.server.service client
199+
- name: trpc.sqlite.server.service
200+
driver_name: sqlite3 # Requires import "github.com/mattn/go-sqlite3"
201+
```
202+
203+
Complete example: [sqlite-example](examples/sqlite/main.go)
204+
205+
### PostgreSQL Integration
206+
207+
Add trpc_go.yaml framework configuration.
91208

209+
Note that service.name uses the four-segment format `trpc.${app}.${server}.${service}`, where `${app}` should be `postgres`.
210+
211+
```yaml
212+
client:
213+
service:
214+
- name: trpc.postgres.server.service
215+
# Reference: https://github.com/jackc/pgx?tab=readme-ov-file#example-usage
216+
target: dsn://postgres://username:password@localhost:5432/database_name
92217
```
93218

94219
### Code Implementation
@@ -136,6 +261,7 @@ Example of logging:
136261
gormDB := gorm.NewClientProxy("trpc.mysql.test.test")
137262
gormDB.Debug().Where("current_owners = ?", "xxxx").Where("id < ?", xxxx).Find(&owners)
138263
```
264+
139265
### Context
140266
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.
141267

@@ -166,6 +292,9 @@ Example:
166292
gormDB.Debug().Where("current_owners = ?", "xxxx").Where("id < ?", xxxx).Find(&owners)
167293
```
168294

295+
### Implementation Details
296+
297+
See the specific [implementation details](docs/architecture.md) of the gorm plugin.
169298

170299
## Implementation Approach
171300

@@ -197,14 +326,39 @@ Thus, the Client becomes a custom connection that satisfies gorm's requirements,
197326

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

329+
## Notes
330+
331+
### Timeout Settings Not Effective
332+
333+
If users add timeout in the framework configuration, the tRPC-Go framework will create a new context when making calls and cancel the context after the call ends. This feature will cause "Context Canceled" errors when reading data from the `Row` interface, so the current plugin will set timeout to zero.
334+
335+
```yaml
336+
client:
337+
service:
338+
- name: trpc.mysql.xxxx.xxxx # initialized as mysql
339+
timeout: 1000 # timeout configuration will not take effect
340+
```
341+
342+
If you need to configure request timeout, you can use context.WithTimeout() to control the context yourself.
343+
If you want to configure connection establishment timeout, connection read/write timeout, you can consider configuring related parameters in the DSN, for example, for MySQL you can configure [`readTimeout`, `writeTimeout` and `timeout`](https://github.com/go-sql-driver/mysql?tab=readme-ov-file#connection-pool-and-timeouts) in the DSN.
344+
345+
```yaml
346+
client:
347+
service:
348+
- name: trpc.mysql.server.service
349+
# Add readTimeout to DSN parameters, reference: https://github.com/go-sql-driver/mysql?tab=readme-ov-file#dsn-data-source-name
350+
target: dsn://root:123456@tcp(127.0.0.1:3306)/mydb?readTimeout=100ms
351+
```
200352

201353
## Related Links:
202354

203355
* Custom Connection in GORM:https://gorm.io/zh_CN/docs/connecting_to_the_database.html
204356

205357
## FAQ
358+
206359
### How to print specific SQL statements and results?
207-
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.
360+
This plugin has implemented the TRPC Logger for GORM. You only need to configure `plugin.database.gorm.logger` configuration (requires plugin version >= v0.2.2) to print specific SQL statements to tRPC-Go logs.
361+
Or you can add `Debug()` before the request to output to logs at Info level.
208362

209363
Example:
210364
```
@@ -222,15 +376,49 @@ gormDB.WithContext(ctx).Where("current_owners = ?", "xxxx").Where("id < ?", xxxx
222376
### How to set the isolation level for transactions?
223377
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.
224378

379+
### Soft delete not working after switching from native gorm to tRPC gorm plugin
380+
381+
This is because gorm changed the soft delete method after the major version upgrade from jinzhu/gorm to gorm.io/gorm.
382+
383+
For the new soft delete method, see the documentation https://gorm.io/docs/delete.html#Soft-Delete
384+
385+
Using gorm.DeleteAt can directly be compatible with jinzhu/gorm soft delete.
386+
387+
### How to handle Context Canceled errors
388+
389+
**Update to the latest version to resolve this issue. The latest version will force timeout to 0.**
390+
391+
This problem is generally caused by setting a global timeout in the trpc framework client. A common trpc framework configuration example is as follows:
392+
393+
```yaml
394+
client:
395+
timeout: 1000 # Need to remove this
396+
service:
397+
- name: xxxxx
398+
protocol: trpc
399+
timeout: 1000 # Set timeout separately for other services
400+
```
401+
402+
The client-level timeout is a global timeout. All trpc clients that don't have a separate timeout setting will use this setting. The old version of this plugin would report errors due to context being canceled as long as timeout was set.
403+
225404
### Currently, only MySQL, ClickHouse, and PostgreSQL are supported.
226405

406+
Parse the `client.service.name` name, which needs to use the standard four-segment name format. For example, `trpc.mysql.xxx.xxx` initializes the `mysql driver`; `trpc.clickhouse.xxx.xxx` initializes the `clickhouse driver`; `trpc.postgres.xxx.xxx` initializes the `postgres driver(pgx)`.
407+
408+
For non-standard four-segment names, it defaults to initializing the `mysql driver`.
409+
227410
### When using GORM transactions, only the Begin method goes through the tRPC call chain.
411+
412+
**Update to the latest version to resolve this issue. v0.1.3 version has resolved this issue.**
413+
228414
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.
229415

230416
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.
231417

232418
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.
233419

420+
> To ensure data consistency, GORM performs write operations (create, update, delete) within transactions. If you don't have this requirement, you can disable it during initialization.
421+
234422
Example:
235423
```go
236424
connPool := gormplugin.NewConnPool("trpc.mysql.test.test")
@@ -244,6 +432,4 @@ gormDB, err := gorm.Open(
244432
SkipDefaultTransaction: true, // Disable GORM transaction optimization
245433
},
246434
)
247-
```
248-
249-
435+
```

0 commit comments

Comments
 (0)