server.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package pgserver
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "net"
  7. "sync"
  8. "time"
  9. "github.com/danfragoso/pizzasql-next/pkg/storage"
  10. )
  11. // Config holds server configuration
  12. type Config struct {
  13. Host string
  14. Port int
  15. MaxConnections int
  16. ReadTimeout time.Duration
  17. WriteTimeout time.Duration
  18. DefaultDatabase string
  19. Quiet bool // Disable query logging
  20. }
  21. // DefaultConfig returns default configuration
  22. func DefaultConfig() *Config {
  23. return &Config{
  24. Host: "localhost",
  25. Port: 5432,
  26. MaxConnections: 100,
  27. ReadTimeout: 30 * time.Second,
  28. WriteTimeout: 30 * time.Second,
  29. DefaultDatabase: "pizzasql",
  30. }
  31. }
  32. // Server represents the PostgreSQL wire protocol server
  33. type Server struct {
  34. config *Config
  35. dbManager *storage.DatabaseManager
  36. listener net.Listener
  37. ctx context.Context
  38. cancel context.CancelFunc
  39. wg sync.WaitGroup
  40. }
  41. // New creates a new PostgreSQL wire protocol server
  42. func New(config *Config, dbManager *storage.DatabaseManager) *Server {
  43. if config == nil {
  44. config = DefaultConfig()
  45. }
  46. ctx, cancel := context.WithCancel(context.Background())
  47. return &Server{
  48. config: config,
  49. dbManager: dbManager,
  50. ctx: ctx,
  51. cancel: cancel,
  52. }
  53. }
  54. // Start starts the server
  55. func (s *Server) Start() error {
  56. addr := fmt.Sprintf("%s:%d", s.config.Host, s.config.Port)
  57. listener, err := net.Listen("tcp", addr)
  58. if err != nil {
  59. return fmt.Errorf("failed to start PostgreSQL server: %w", err)
  60. }
  61. s.listener = listener
  62. log.Printf("PostgreSQL wire protocol server listening on %s", addr)
  63. // Accept connections
  64. for {
  65. conn, err := listener.Accept()
  66. if err != nil {
  67. select {
  68. case <-s.ctx.Done():
  69. // Server is shutting down
  70. return nil
  71. default:
  72. log.Printf("Error accepting connection: %v", err)
  73. continue
  74. }
  75. }
  76. // Handle connection in a goroutine
  77. s.wg.Add(1)
  78. go func() {
  79. defer s.wg.Done()
  80. s.handleConnection(conn)
  81. }()
  82. }
  83. }
  84. // handleConnection handles a client connection
  85. func (s *Server) handleConnection(conn net.Conn) {
  86. // Don't set static deadlines - let the connection be persistent
  87. // Timeouts will be handled by context cancellation if needed
  88. c := NewConnection(conn, s.dbManager, s.config.Quiet)
  89. if err := c.Handle(); err != nil {
  90. log.Printf("Connection error: %v", err)
  91. }
  92. }
  93. // Shutdown gracefully shuts down the server
  94. func (s *Server) Shutdown(ctx context.Context) error {
  95. log.Println("Shutting down PostgreSQL server...")
  96. // Cancel context to stop accepting new connections
  97. s.cancel()
  98. // Close listener
  99. if s.listener != nil {
  100. if err := s.listener.Close(); err != nil {
  101. log.Printf("Error closing listener: %v", err)
  102. }
  103. }
  104. // Wait for connections to finish with timeout
  105. done := make(chan struct{})
  106. go func() {
  107. s.wg.Wait()
  108. close(done)
  109. }()
  110. select {
  111. case <-done:
  112. log.Println("PostgreSQL server stopped gracefully")
  113. return nil
  114. case <-ctx.Done():
  115. log.Println("PostgreSQL server shutdown timeout")
  116. return ctx.Err()
  117. }
  118. }
  119. // Addr returns the server address
  120. func (s *Server) Addr() string {
  121. if s.listener != nil {
  122. return s.listener.Addr().String()
  123. }
  124. return fmt.Sprintf("%s:%d", s.config.Host, s.config.Port)
  125. }