Skip to content

Commit b37e73d

Browse files
authored
Merge pull request #107 from fly-apps/remove_pgbouncer
Remove pgbouncer in favor of haproxy for base setup.
2 parents 33866ff + fd58745 commit b37e73d

File tree

16 files changed

+150
-485
lines changed

16 files changed

+150
-485
lines changed

Dockerfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ LABEL fly.pg-version=${PG_VERSION}
3131
LABEL fly.pg-manager=repmgr
3232

3333
RUN apt-get update && apt-get install --no-install-recommends -y \
34-
ca-certificates iproute2 postgresql-$PG_MAJOR_VERSION-repmgr curl bash dnsutils vim procps jq pgbouncer ssh \
34+
ca-certificates iproute2 postgresql-$PG_MAJOR_VERSION-repmgr curl bash dnsutils vim haproxy socat procps ssh \
3535
&& apt autoremove -y
3636

3737
RUN apt-get update && apt-get install --no-install-recommends -y \
@@ -47,5 +47,4 @@ RUN usermod -d /data postgres
4747

4848
EXPOSE 5432
4949

50-
5150
CMD ["start"]

Dockerfile-timescaledb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ LABEL fly.pg-version=${PG_VERSION}
3131
LABEL fly.pg-manager=repmgr
3232

3333
RUN apt-get update && apt-get install --no-install-recommends -y \
34-
ca-certificates iproute2 postgresql-$PG_MAJOR_VERSION-repmgr curl bash dnsutils vim procps jq pgbouncer ssh \
34+
ca-certificates iproute2 postgresql-$PG_MAJOR_VERSION-repmgr curl bash dnsutils vim haproxy socat procps ssh \
3535
&& apt autoremove -y
3636

3737
RUN echo "deb https://packagecloud.io/timescale/timescaledb/debian/ $(cat /etc/os-release | grep VERSION_CODENAME | cut -d'=' -f2) main" > /etc/apt/sources.list.d/timescaledb.list \

bin/restart-haproxy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
3+
PID=$(echo "show info" | socat /var/run/haproxy/haproxy.sock stdio | grep Pid | awk '{print $2}')
4+
kill -SIGTERM $PID

cmd/event_handler/main.go

Lines changed: 6 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77
"fmt"
88
"log"
99
"os"
10-
"strconv"
11-
"time"
1210

1311
"github.com/fly-apps/postgres-flex/internal/flypg"
1412
"github.com/jackc/pgx/v5"
@@ -23,7 +21,6 @@ func main() {
2321
// old primary. In the events that we subscribe to it's always either empty or the new primary.
2422
// In the future if we subscribe to repmgrd_failover_promote, then we would have to change this
2523
// name.
26-
newPrimary := flag.String("new-node-id", "", "the new primary node id")
2724
success := flag.String("success", "", "success (1) failure (0)")
2825
details := flag.String("details", "", "details")
2926
flag.Parse()
@@ -39,69 +36,14 @@ func main() {
3936
log.SetOutput(logFile)
4037
log.Printf("event: %s, node: %d, success: %s, details: %s\n", *event, *nodeID, *success, *details)
4138

42-
switch *event {
43-
case "repmgrd_failover_promote", "standby_promote":
44-
// TODO - Need to figure out what to do when success == 0.
45-
retry := 0
46-
maxRetries := 5
47-
success := false
48-
49-
for retry < maxRetries {
50-
if err := reconfigurePGBouncer(*nodeID); err != nil {
51-
log.Printf("%s - failed to reconfigure pgbouncer: %s. (attempt: %d)\n", *event, err, retry)
52-
retry++
53-
time.Sleep(1 * time.Second)
54-
continue
55-
}
56-
success = true
57-
break
58-
}
59-
60-
if success {
61-
log.Printf("%s - successfully reconfigured pgbouncer to target: %d\n", *event, *nodeID)
62-
os.Exit(0)
63-
} else {
64-
log.Printf("%s - failed to reconfigured pgbouncer to target: %d\n", *event, *nodeID)
65-
os.Exit(1)
66-
}
67-
68-
case "standby_follow":
69-
newMemberID, err := strconv.Atoi(*newPrimary)
70-
if err != nil {
71-
log.Printf("failed to parse newMemberID %s: %s\n", *newPrimary, err)
72-
os.Exit(1)
73-
}
74-
75-
retry := 0
76-
maxRetries := 5
77-
success := false
78-
79-
for retry < maxRetries {
80-
if err := reconfigurePGBouncer(newMemberID); err != nil {
81-
log.Printf("%s - failed to reconfigure pgbouncer: %s. (attempt: %d)\n", *event, err, retry)
82-
retry++
83-
time.Sleep(1 * time.Second)
84-
continue
85-
}
86-
success = true
87-
break
88-
}
89-
90-
if success {
91-
log.Printf("%s - successfully reconfigured pgbouncer to target: %d\n", *event, newMemberID)
92-
os.Exit(0)
93-
} else {
94-
log.Printf("%s - failed to reconfigured pgbouncer to target: %d\n", *event, newMemberID)
95-
os.Exit(1)
96-
}
39+
node, err := flypg.NewNode()
40+
if err != nil {
41+
log.Printf("failed to initialize node: %s", err)
42+
os.Exit(1)
43+
}
9744

45+
switch *event {
9846
case "child_node_disconnect", "child_node_reconnect", "child_node_new_connect":
99-
node, err := flypg.NewNode()
100-
if err != nil {
101-
log.Printf("failed to initialize node: %s", err)
102-
os.Exit(1)
103-
}
104-
10547
conn, err := node.RepMgr.NewLocalConnection(ctx)
10648
if err != nil {
10749
log.Printf("failed to open local connection: %s", err)
@@ -132,29 +74,6 @@ func main() {
13274
}
13375
}
13476

135-
func reconfigurePGBouncer(id int) error {
136-
node, err := flypg.NewNode()
137-
if err != nil {
138-
return fmt.Errorf("failed to reference node: %s", err)
139-
}
140-
141-
conn, err := node.RepMgr.NewLocalConnection(context.TODO())
142-
if err != nil {
143-
return fmt.Errorf("failed to establish connection with local pg: %s", err)
144-
}
145-
146-
member, err := node.RepMgr.MemberByID(context.TODO(), conn, id)
147-
if err != nil {
148-
return err
149-
}
150-
151-
if err := node.PGBouncer.ConfigurePrimary(context.TODO(), member.Hostname, true); err != nil {
152-
return fmt.Errorf("failed to reconfigure pgbouncer primary %s", err)
153-
}
154-
155-
return nil
156-
}
157-
15877
func evaluateClusterState(ctx context.Context, conn *pgx.Conn, node *flypg.Node) error {
15978
standbys, err := node.RepMgr.StandbyMembers(ctx, conn)
16079
if err != nil {
@@ -195,9 +114,5 @@ func evaluateClusterState(ctx context.Context, conn *pgx.Conn, node *flypg.Node)
195114
}
196115
}
197116

198-
if err := node.PGBouncer.ConfigurePrimary(ctx, primary, true); err != nil {
199-
return fmt.Errorf("failed to reconfigure pgbouncer primary %s", err)
200-
}
201-
202117
return nil
203118
}

cmd/start/main.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,13 @@ func main() {
4848

4949
svisor.AddProcess("postgres", fmt.Sprintf("gosu postgres postgres -D %s -p %d", node.DataDir, node.Port))
5050

51-
svisor.AddProcess("pgbouncer", fmt.Sprintf("pgbouncer %s/pgbouncer.ini", node.PGBouncer.ConfigPath),
52-
supervisor.WithRestart(0, 1*time.Second),
53-
)
51+
proxyEnv := map[string]string{
52+
"FLY_APP_NAME": os.Getenv("FLY_APP_NAME"),
53+
"PRIMARY_REGION": os.Getenv("PRIMARY_REGION"),
54+
"PG_LISTEN_ADDRESS": node.PrivateIP,
55+
}
56+
svisor.AddProcess("proxy", "/usr/sbin/haproxy -W -db -f /fly/haproxy.cfg", supervisor.WithEnv(proxyEnv), supervisor.WithRestart(0, 1*time.Second))
57+
5458
svisor.AddProcess("repmgrd", fmt.Sprintf("gosu postgres repmgrd -f %s --daemonize=false", node.RepMgr.ConfigPath),
5559
supervisor.WithRestart(0, 5*time.Second),
5660
)

config/haproxy.cfg

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
global
2+
maxconn 1000
3+
stats socket /run/haproxy/haproxy.sock mode 660 level admin
4+
stats timeout 2m # Wait up to 2 minutes for input
5+
6+
defaults
7+
log global
8+
mode tcp
9+
retries 2
10+
timeout client 30m
11+
timeout connect 4s
12+
timeout server 30m
13+
timeout check 5s
14+
15+
resolvers flydns
16+
nameserver dns1 [fdaa::3]:53
17+
accepted_payload_size 8192 # allow larger DNS payloads
18+
19+
frontend ft_postgresql
20+
mode tcp
21+
bind *:5432
22+
bind :::5432
23+
default_backend bk_db
24+
25+
frontend stats
26+
mode http
27+
bind :::8404
28+
stats enable
29+
stats uri /stats
30+
stats refresh 10s
31+
32+
backend bk_db
33+
balance roundrobin
34+
option httpchk GET /flycheck/role
35+
http-check expect string primary
36+
http-check disable-on-404
37+
server-template pg 10 $PRIMARY_REGION.$FLY_APP_NAME.internal:5433 check port 5500 resolvers flydns resolve-prefer ipv6 init-addr none on-marked-down shutdown-sessions
38+
server pg [$PG_LISTEN_ADDRESS]:5433 check backup port 5500 on-marked-down shutdown-sessions

internal/api/handle_admin.go

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ func handleReadonlyState(w http.ResponseWriter, r *http.Request) {
2424
renderJSON(w, res, http.StatusOK)
2525
}
2626

27+
func handleHaproxyRestart(w http.ResponseWriter, r *http.Request) {
28+
if err := flypg.RestartHaproxy(); err != nil {
29+
renderErr(w, err)
30+
return
31+
}
32+
33+
res := &Response{
34+
Result: true,
35+
}
36+
37+
renderJSON(w, res, http.StatusOK)
38+
}
39+
2740
func handleEnableReadonly(w http.ResponseWriter, r *http.Request) {
2841
res := &Response{
2942
Result: true,
@@ -251,33 +264,6 @@ func (s *Server) handleViewPostgresSettings(w http.ResponseWriter, r *http.Reque
251264
renderJSON(w, resp, http.StatusOK)
252265
}
253266

254-
func (s *Server) handleViewBouncerSettings(w http.ResponseWriter, r *http.Request) {
255-
all, err := s.node.PGBouncer.CurrentConfig()
256-
if err != nil {
257-
renderErr(w, err)
258-
return
259-
}
260-
261-
var in []string
262-
263-
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
264-
renderErr(w, err)
265-
return
266-
}
267-
268-
out := map[string]interface{}{}
269-
270-
for key := range all {
271-
val := all[key]
272-
if slices.Contains(in, key) {
273-
out[key] = val
274-
}
275-
}
276-
277-
resp := &Response{Result: out}
278-
renderJSON(w, resp, http.StatusOK)
279-
}
280-
281267
func (s *Server) handleViewRepmgrSettings(w http.ResponseWriter, r *http.Request) {
282268
all, err := s.node.RepMgr.CurrentConfig()
283269
if err != nil {

internal/api/handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ func (s *Server) Handler() http.Handler {
4848
r.Get("/readonly/enable", handleEnableReadonly)
4949
r.Get("/readonly/disable", handleDisableReadonly)
5050
r.Get("/readonly/state", handleReadonlyState)
51+
r.Get("/haproxy/restart", handleHaproxyRestart)
5152

5253
r.Get("/role", handleRole)
5354
r.Get("/settings/view/postgres", s.handleViewPostgresSettings)
54-
r.Get("/settings/view/pgbouncer", s.handleViewBouncerSettings)
5555
r.Get("/settings/view/repmgr", s.handleViewRepmgrSettings)
5656
r.Post("/settings/update/postgres", s.handleUpdatePostgresSettings)
5757
r.Post("/settings/apply", s.handleApplyConfig)

internal/flypg/haproxy.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package flypg
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/fly-apps/postgres-flex/internal/utils"
7+
)
8+
9+
func RestartHaproxy() error {
10+
if err := utils.RunCommand("restart-haproxy", "root"); err != nil {
11+
return fmt.Errorf("failed to restart haproxy: %s", err)
12+
}
13+
14+
return nil
15+
}

0 commit comments

Comments
 (0)