Skip to content

Commit c7b329e

Browse files
Refactoring: database connection
1 parent c161049 commit c7b329e

File tree

8 files changed

+380
-77
lines changed

8 files changed

+380
-77
lines changed

README.md

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This library partially implements DipDup framework features and can be used for
77

88
## Packages
99

10-
#### `cmdline`
10+
### `cmdline`
1111

1212
Command line argument parser, compatible with [DipDup CLI](https://docs.dipdup.net/command-line).
1313

@@ -20,7 +20,7 @@ if args.Help {
2020
}
2121
```
2222

23-
#### `config`
23+
### `config`
2424

2525
DipDup YAML [configuration](https://docs.dipdup.net/config-file-reference) parser. You can validate config by `validate` tag from [validator package](https://github.com/go-playground/validator).
2626

@@ -48,7 +48,7 @@ if err := config.Parse("config.yaml", &cfg); err != nil {
4848
}
4949
```
5050

51-
#### `node`
51+
### `node`
5252

5353
Simple Tezos RPC API wrapper.
5454

@@ -58,29 +58,75 @@ import "github.com/dipdup-net/go-lib/node"
5858
rpc := node.NewNodeRPC(url, node.WithTimeout(timeout))
5959
```
6060

61-
#### `state`
61+
### `database`
62+
63+
Managing DipDup database connection. Default interface contains custom method `Connect` and extends 3 interfaces `driver.Pinger`, `StateRepository` and `io.Closer`.
6264

63-
Managing DipDup index state.
6465

6566
```go
66-
import "github.com/dipdup-net/go-lib/state"
67+
// Database -
68+
type Database interface {
69+
Connect(ctx context.Context, cfg config.Database) error
70+
71+
StateRepository
72+
73+
driver.Pinger
74+
io.Closer
75+
}
6776

68-
s := state.State{}
77+
// StateRepository -
78+
type StateRepository interface {
79+
State(name string) (State, error)
80+
UpdateState(state State) error
81+
CreateState(state State) error
82+
DeleteState(state State) error
83+
}
6984
```
7085

7186
where `State` structure is:
87+
7288
```go
7389
// State -
7490
type State struct {
75-
IndexName string `gorm:"primaryKey"`
76-
IndexType string
77-
Hash string
78-
Level uint64
91+
//nolint
92+
tableName struct{} `gorm:"-" pg:"dipdup_state" json:"-"`
93+
94+
IndexName string `gorm:"primaryKey" pg:",pk" json:"index_name"`
95+
IndexType string `json:"index_type"`
96+
Hash string `json:"hash,omitempty"`
97+
Level uint64 `json:"level"`
7998
UpdatedAt int `gorm:"autoUpdateTime"`
8099
}
81100
```
82101

83-
#### `tzkt`
102+
There are 2 default implementations of `Database` interface:
103+
* `Gorm` - database connection via [gorm](https://gorm.io/)
104+
* `PgGo` - database connection via [pg-go](https://pg.uptrace.dev/)
105+
106+
There is method `Wait` which waiting until database connection will be established.
107+
108+
Exaple of usage:
109+
110+
```go
111+
ctx, cancel := context.WithCancel(context.Background())
112+
defer cancel()
113+
114+
db := database.NewPgGo()
115+
if err := db.Connect(ctx, cfg); err != nil {
116+
panic(err)
117+
}
118+
119+
database.Wait(ctx, db, 5*time.Second)
120+
121+
var yourModel struct{}
122+
conn := db.DB()
123+
if err := conn.WithContext(ctx).Model(&yourModel).Limit(10).Select(); err != nil {
124+
panic(err)
125+
}
126+
```
127+
128+
129+
### `tzkt`
84130

85131
TzKT API and Events wrapper.
86132
Read more about events and SignalR in the [doc](https://github.com/dipdup-net/go-lib/blob/master/tzkt/events/README.md)

database/db.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package database
2+
3+
import (
4+
"context"
5+
"database/sql/driver"
6+
"io"
7+
"time"
8+
9+
"github.com/dipdup-net/go-lib/config"
10+
"github.com/pkg/errors"
11+
"github.com/sirupsen/logrus"
12+
)
13+
14+
// Database -
15+
type Database interface {
16+
Connect(ctx context.Context, cfg config.Database) error
17+
18+
StateRepository
19+
20+
driver.Pinger
21+
io.Closer
22+
}
23+
24+
// errors
25+
var (
26+
ErrConnectionIsNotInitialized = errors.New("connection is not initialized")
27+
ErrUnsupportedDatabaseType = errors.New("unsupported database type")
28+
)
29+
30+
// Wait -
31+
func Wait(ctx context.Context, db driver.Pinger, checkPeriod time.Duration) {
32+
logrus.Info("Waiting database is up and runnning")
33+
if err := db.Ping(ctx); err == nil {
34+
return
35+
}
36+
37+
ticker := time.NewTicker(checkPeriod)
38+
for {
39+
select {
40+
case <-ctx.Done():
41+
return
42+
case <-ticker.C:
43+
if err := db.Ping(ctx); err == nil {
44+
logrus.Warn(err)
45+
continue
46+
}
47+
return
48+
}
49+
}
50+
}
Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package state
1+
package database
22

33
import (
44
"context"
@@ -9,30 +9,30 @@ import (
99

1010
"github.com/dipdup-net/go-lib/config"
1111
"github.com/pkg/errors"
12-
"github.com/sirupsen/logrus"
1312
"gorm.io/driver/mysql"
1413
"gorm.io/driver/postgres"
1514
"gorm.io/driver/sqlite"
1615
"gorm.io/gorm"
1716
"gorm.io/gorm/logger"
1817
)
1918

20-
// CheckConnection
21-
func CheckConnection(db *gorm.DB) error {
22-
sql, err := db.DB()
23-
if err != nil {
24-
return err
25-
}
19+
// Gorm -
20+
type Gorm struct {
21+
conn *gorm.DB
22+
}
2623

27-
if err = sql.Ping(); err != nil {
28-
return err
29-
}
24+
// NewGorm -
25+
func NewGorm() *Gorm {
26+
return new(Gorm)
27+
}
3028

31-
return nil
29+
// DB -
30+
func (db *Gorm) DB() *gorm.DB {
31+
return db.conn
3232
}
3333

34-
// OpenConnection -
35-
func OpenConnection(ctx context.Context, cfg config.Database) (*gorm.DB, error) {
34+
// Connect -
35+
func (db *Gorm) Connect(ctx context.Context, cfg config.Database) error {
3636
var dialector gorm.Dialector
3737
switch cfg.Kind {
3838
case config.DBKindSqlite:
@@ -56,10 +56,10 @@ func OpenConnection(ctx context.Context, cfg config.Database) (*gorm.DB, error)
5656
}
5757
dialector = mysql.Open(connString)
5858
default:
59-
return nil, errors.Errorf("Unsupported database kind %s", cfg.Kind)
59+
return errors.Wrap(ErrUnsupportedDatabaseType, cfg.Kind)
6060
}
6161

62-
db, err := gorm.Open(dialector, &gorm.Config{
62+
conn, err := gorm.Open(dialector, &gorm.Config{
6363
SkipDefaultTransaction: true,
6464
PrepareStmt: true,
6565
Logger: logger.New(
@@ -71,31 +71,56 @@ func OpenConnection(ctx context.Context, cfg config.Database) (*gorm.DB, error)
7171
),
7272
})
7373
if err != nil {
74-
return nil, err
74+
return err
7575
}
76+
db.conn = conn
7677

77-
checkHealth(ctx, db)
78+
return nil
79+
}
7880

79-
return db, nil
81+
// Close -
82+
func (db *Gorm) Close() error {
83+
sql, err := db.conn.DB()
84+
if err != nil {
85+
return err
86+
}
87+
return sql.Close()
8088
}
8189

82-
func checkHealth(ctx context.Context, db *gorm.DB) {
83-
logrus.Info("Waiting database is up and runnning")
84-
if err := CheckConnection(db); err == nil {
85-
return
90+
// Ping -
91+
func (db *Gorm) Ping(ctx context.Context) error {
92+
if db.conn == nil {
93+
return ErrConnectionIsNotInitialized
94+
}
95+
sql, err := db.conn.DB()
96+
if err != nil {
97+
return err
8698
}
8799

88-
ticker := time.NewTicker(5 * time.Second)
89-
for {
90-
select {
91-
case <-ctx.Done():
92-
return
93-
case <-ticker.C:
94-
if err := CheckConnection(db); err != nil {
95-
logrus.Warn(err)
96-
continue
97-
}
98-
return
99-
}
100+
if err = sql.PingContext(ctx); err != nil {
101+
return err
100102
}
103+
104+
return nil
105+
}
106+
107+
// State -
108+
func (db *Gorm) State(indexName string) (s State, err error) {
109+
err = db.conn.Where("index_name = ?", indexName).First(&s).Error
110+
return
111+
}
112+
113+
// CreateState -
114+
func (db *Gorm) CreateState(s State) error {
115+
return db.conn.Create(&s).Error
116+
}
117+
118+
// UpdateState -
119+
func (db *Gorm) UpdateState(s State) error {
120+
return db.conn.Save(&s).Error
121+
}
122+
123+
// DeleteState -
124+
func (db *Gorm) DeleteState(s State) error {
125+
return db.conn.Delete(&s).Error
101126
}

database/pg.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package database
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/dipdup-net/go-lib/config"
8+
pg "github.com/go-pg/pg/v10"
9+
"github.com/pkg/errors"
10+
)
11+
12+
// PgGo -
13+
type PgGo struct {
14+
conn *pg.DB
15+
}
16+
17+
// NewPgGo -
18+
func NewPgGo() *PgGo {
19+
return new(PgGo)
20+
}
21+
22+
// DB -
23+
func (db *PgGo) DB() *pg.DB {
24+
return db.conn
25+
}
26+
27+
// Connect -
28+
func (db *PgGo) Connect(ctx context.Context, cfg config.Database) error {
29+
if cfg.Kind != config.DBKindPostgres {
30+
return errors.Wrap(ErrUnsupportedDatabaseType, cfg.Kind)
31+
}
32+
var conn *pg.DB
33+
if cfg.Path != "" {
34+
opt, err := pg.ParseURL(cfg.Path)
35+
if err != nil {
36+
return err
37+
}
38+
conn = pg.Connect(opt)
39+
} else {
40+
conn = pg.Connect(&pg.Options{
41+
Addr: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
42+
User: cfg.User,
43+
Password: cfg.Password,
44+
Database: cfg.Database,
45+
})
46+
}
47+
db.conn = conn
48+
return nil
49+
}
50+
51+
// Close -
52+
func (db *PgGo) Close() error {
53+
return db.conn.Close()
54+
}
55+
56+
// Ping -
57+
func (db *PgGo) Ping(ctx context.Context) error {
58+
if db.conn == nil {
59+
return ErrConnectionIsNotInitialized
60+
}
61+
return db.conn.Ping(ctx)
62+
}
63+
64+
// State -
65+
func (db *PgGo) State(indexName string) (s State, err error) {
66+
err = db.conn.Model(&s).Where("index_name = ?", indexName).Limit(1).Select()
67+
return
68+
}
69+
70+
// CreateState -
71+
func (db *PgGo) CreateState(s State) error {
72+
_, err := db.conn.Model(&s).Insert()
73+
return err
74+
}
75+
76+
// UpdateState -
77+
func (db *PgGo) UpdateState(s State) error {
78+
_, err := db.conn.Model(&s).WherePK().Update()
79+
return err
80+
}
81+
82+
// DeleteState -
83+
func (db *PgGo) DeleteState(s State) error {
84+
_, err := db.conn.Model(&s).WherePK().Delete()
85+
return err
86+
}

0 commit comments

Comments
 (0)