-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmain.go
203 lines (164 loc) · 3.8 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package main
import (
"fmt"
"sync"
"time"
"database/sql"
"os"
"log"
_ "github.com/go-sql-driver/mysql"
)
var (
dbUser = os.Getenv("DBUSER")
dbPass = os.Getenv("DBPASS")
dbHost = "127.0.0.1:3300"
dbName = "test_db"
// connection string to the DB
dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s", dbUser, dbPass, dbHost, dbName)
)
// open connection to the DB
func createNewConnection() (*sql.DB, error){
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal("Error opening database connection: ", err)
}
err = db.Ping()
if err != nil {
log.Fatal("Error pinging the database: ", err)
}
return db, nil
}
// simulate database operation
func simulateDBOperation (db *sql.DB) error {
_, err := db.Exec("SELECT SLEEP(0.01);")
return err
}
func benchmarkNonPool(n int) (time.Duration, error){
start := time.Now()
var wg sync.WaitGroup
var once sync.Once
var operr error
helper := func (){
defer wg.Done()
conn, err := createNewConnection()
if err != nil {
once.Do(func() {operr = err})
return
}
defer conn.Close()
if err := simulateDBOperation(conn); err != nil {
once.Do(func(){
operr = err
})
}
}
for i:= 0; i < n; i++ {
wg.Add(1)
go helper()
}
// wait for all goroutines to complete
wg.Wait()
return time.Since(start), operr
}
// Conection pool struct to manage DB connections
type ConnectionPool struct {
mu sync.Mutex
channel chan interface{} // channel represent s blocking queue
conns []*sql.DB
}
func NewPool(maxConn int)(*ConnectionPool, error){
pool := &ConnectionPool{
conns : make([]*sql.DB, 0, maxConn),
channel : make(chan interface{}, maxConn),
}
for i:=0; i<maxConn; i++{
conn, err := createNewConnection()
if err != nil {
return nil, err
}
pool.conns = append(pool.conns, conn)
pool.channel <- nil // indicates connection is ready
}
return pool, nil
}
func (pool *ConnectionPool) Get()(*sql.DB, error){
// Block until there is an available connection
<-pool.channel
pool.mu.Lock()
defer pool.mu.Unlock()
conn := pool.conns[0] // Get the connection
pool.conns = pool.conns[1:] // Remove connection from the pool
return conn, nil
}
func (pool *ConnectionPool) Put(conn *sql.DB) {
pool.mu.Lock()
defer pool.mu.Unlock()
pool.conns = append(pool.conns, conn)
// signal one connection is available
pool.channel <- nil
}
func (pool *ConnectionPool) Close() {
pool.mu.Lock()
defer pool.mu.Unlock()
for _, conn := range pool.conns{
conn.Close()
}
pool.conns = nil
}
func benchmarkPool(n int) (time.Duration, error){
// creating a connection pool
pool, err := NewPool(100)
if err != nil{
return 0, fmt.Errorf("Error intializing connection pool: %w", err)
}
start := time.Now()
var wg sync.WaitGroup
var once sync.Once
var operr error
helper := func(){
defer wg.Done()
// get thread from the connection pool
conn, err := pool.Get()
if err != nil {
once.Do(func(){
operr = err
})
}
if err := simulateDBOperation(conn); err != nil {
once.Do(func(){
operr = err
})
}
// return thread to the connection pool
pool.Put(conn)
}
for i:=0; i<n; i++ {
wg.Add(1)
go helper()
}
wg.Wait()
// close the connection pool
pool.Close()
return time.Since(start), operr
}
func main(){
tests := []int{10, 100, 200, 300, 500, 1000}
fmt.Println("\nStarting non-pool benchmarks:")
for _, n := range tests {
elapsed, err := benchmarkNonPool(n)
if err != nil {
fmt.Printf("Error running non-pool benchmark for %d threads: %v\n", n, err)
} else {
fmt.Printf("Non-pool time for %d threads: %v\n", n, elapsed)
}
}
fmt.Println("\nStarting pool benchmarks:")
for _, n := range tests {
elapsed, err := benchmarkPool(n)
if err != nil {
fmt.Printf("Error running pool benchmark for %d threads: %v\n", n, err)
} else {
fmt.Printf("Pool time for %d threads: %v\n", n, elapsed)
}
}
}