2
0

kv.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. package storage
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "net"
  7. "strings"
  8. "sync"
  9. "time"
  10. )
  11. // KVClient represents a connection to PizzaKV.
  12. type KVClient struct {
  13. conn net.Conn
  14. reader *bufio.Reader
  15. writer *bufio.Writer
  16. mu sync.Mutex
  17. }
  18. // NewKVClient creates a new KV client connected to the given address.
  19. // addr may be "host:port" for TCP or "unix:<path>" for a Unix socket.
  20. func NewKVClient(addr string) (*KVClient, error) {
  21. network, target := parseAddr(addr)
  22. conn, err := net.Dial(network, target)
  23. if err != nil {
  24. return nil, fmt.Errorf("failed to connect to PizzaKV: %w", err)
  25. }
  26. return &KVClient{
  27. conn: conn,
  28. reader: bufio.NewReader(conn),
  29. writer: bufio.NewWriter(conn),
  30. }, nil
  31. }
  32. // parseAddr splits an addr string into (network, address).
  33. // "unix:<path>" → ("unix", "<path>"), anything else → ("tcp", addr).
  34. func parseAddr(addr string) (string, string) {
  35. if strings.HasPrefix(addr, "unix:") {
  36. return "unix", strings.TrimPrefix(addr, "unix:")
  37. }
  38. return "tcp", addr
  39. }
  40. // Close closes the connection.
  41. func (c *KVClient) Close() error {
  42. c.mu.Lock()
  43. defer c.mu.Unlock()
  44. if c.conn != nil {
  45. return c.conn.Close()
  46. }
  47. return nil
  48. }
  49. // SetDeadline sets the read/write deadline.
  50. func (c *KVClient) SetDeadline(t time.Time) error {
  51. return c.conn.SetDeadline(t)
  52. }
  53. // Write stores a key-value pair.
  54. func (c *KVClient) Write(key, value string) error {
  55. c.mu.Lock()
  56. defer c.mu.Unlock()
  57. cmd := fmt.Sprintf("write %s|%s\r", key, value)
  58. if _, err := c.writer.WriteString(cmd); err != nil {
  59. return fmt.Errorf("write command failed: %w", err)
  60. }
  61. if err := c.writer.Flush(); err != nil {
  62. return fmt.Errorf("flush failed: %w", err)
  63. }
  64. resp, err := c.reader.ReadString('\r')
  65. if err != nil {
  66. return fmt.Errorf("read response failed: %w", err)
  67. }
  68. resp = strings.TrimSuffix(resp, "\r")
  69. if resp != "success" {
  70. return fmt.Errorf("write failed: %s", resp)
  71. }
  72. return nil
  73. }
  74. // Read retrieves a value by key.
  75. func (c *KVClient) Read(key string) (string, error) {
  76. c.mu.Lock()
  77. defer c.mu.Unlock()
  78. cmd := fmt.Sprintf("read %s\r", key)
  79. if _, err := c.writer.WriteString(cmd); err != nil {
  80. return "", fmt.Errorf("read command failed: %w", err)
  81. }
  82. if err := c.writer.Flush(); err != nil {
  83. return "", fmt.Errorf("flush failed: %w", err)
  84. }
  85. resp, err := c.reader.ReadString('\r')
  86. if err != nil {
  87. return "", fmt.Errorf("read response failed: %w", err)
  88. }
  89. resp = strings.TrimSuffix(resp, "\r")
  90. if resp == "error" {
  91. return "", ErrKeyNotFound
  92. }
  93. return resp, nil
  94. }
  95. // Delete removes a key.
  96. func (c *KVClient) Delete(key string) error {
  97. c.mu.Lock()
  98. defer c.mu.Unlock()
  99. cmd := fmt.Sprintf("delete %s\r", key)
  100. if _, err := c.writer.WriteString(cmd); err != nil {
  101. return fmt.Errorf("delete command failed: %w", err)
  102. }
  103. if err := c.writer.Flush(); err != nil {
  104. return fmt.Errorf("flush failed: %w", err)
  105. }
  106. resp, err := c.reader.ReadString('\r')
  107. if err != nil {
  108. return fmt.Errorf("read response failed: %w", err)
  109. }
  110. resp = strings.TrimSuffix(resp, "\r")
  111. if resp != "success" && resp != "error" {
  112. return fmt.Errorf("delete failed: %s", resp)
  113. }
  114. return nil
  115. }
  116. // Reads retrieves all values with a key prefix.
  117. func (c *KVClient) Reads(prefix string) ([]string, error) {
  118. c.mu.Lock()
  119. defer c.mu.Unlock()
  120. cmd := fmt.Sprintf("reads %s\r", prefix)
  121. if _, err := c.writer.WriteString(cmd); err != nil {
  122. return nil, fmt.Errorf("reads command failed: %w", err)
  123. }
  124. if err := c.writer.Flush(); err != nil {
  125. return nil, fmt.Errorf("flush failed: %w", err)
  126. }
  127. resp, err := c.reader.ReadString('\r')
  128. if err != nil {
  129. return nil, fmt.Errorf("read response failed: %w", err)
  130. }
  131. resp = strings.TrimSuffix(resp, "\r")
  132. if resp == "" {
  133. return nil, nil
  134. }
  135. values := strings.Split(resp, "\n")
  136. result := make([]string, 0, len(values))
  137. for _, v := range values {
  138. if v != "" {
  139. result = append(result, v)
  140. }
  141. }
  142. return result, nil
  143. }
  144. // IsAlive checks if the connection is still alive.
  145. func (c *KVClient) IsAlive() bool {
  146. c.mu.Lock()
  147. defer c.mu.Unlock()
  148. if c.conn == nil {
  149. return false
  150. }
  151. // Try to set a short deadline and do a no-op check
  152. c.conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
  153. defer c.conn.SetReadDeadline(time.Time{})
  154. one := make([]byte, 1)
  155. c.conn.SetReadDeadline(time.Now().Add(1 * time.Millisecond))
  156. _, err := c.conn.Read(one)
  157. if err != nil {
  158. if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
  159. return true // Timeout is expected
  160. }
  161. return false
  162. }
  163. return true
  164. }
  165. // ErrKeyNotFound is returned when a key doesn't exist.
  166. var ErrKeyNotFound = fmt.Errorf("key not found")
  167. // KVPool manages a pool of KV client connections.
  168. type KVPool struct {
  169. addr string
  170. pool chan *KVClient
  171. size int
  172. timeout time.Duration
  173. mu sync.Mutex
  174. closed bool
  175. }
  176. // NewKVPool creates a new connection pool.
  177. func NewKVPool(addr string, size int, timeout time.Duration) (*KVPool, error) {
  178. p := &KVPool{
  179. addr: addr,
  180. pool: make(chan *KVClient, size),
  181. size: size,
  182. timeout: timeout,
  183. }
  184. // Pre-create connections
  185. for i := 0; i < size; i++ {
  186. client, err := NewKVClient(addr)
  187. if err != nil {
  188. // Close any created connections
  189. p.Close()
  190. return nil, fmt.Errorf("failed to create connection pool: %w", err)
  191. }
  192. p.pool <- client
  193. }
  194. return p, nil
  195. }
  196. // Get retrieves a connection from the pool.
  197. func (p *KVPool) Get() (*KVClient, error) {
  198. p.mu.Lock()
  199. if p.closed {
  200. p.mu.Unlock()
  201. return nil, fmt.Errorf("pool is closed")
  202. }
  203. p.mu.Unlock()
  204. select {
  205. case client := <-p.pool:
  206. // Validate connection
  207. if client != nil && client.conn != nil {
  208. if p.timeout > 0 {
  209. client.SetDeadline(time.Now().Add(p.timeout))
  210. }
  211. return client, nil
  212. }
  213. // Stale connection — replace with a fresh one
  214. return NewKVClient(p.addr)
  215. case <-time.After(30 * time.Second):
  216. return nil, fmt.Errorf("kv pool timeout: no connection available after 30s")
  217. }
  218. }
  219. // Put returns a connection to the pool.
  220. func (p *KVPool) Put(client *KVClient) {
  221. if client == nil {
  222. return
  223. }
  224. p.mu.Lock()
  225. if p.closed {
  226. p.mu.Unlock()
  227. client.Close()
  228. return
  229. }
  230. p.mu.Unlock()
  231. // Clear deadline
  232. client.SetDeadline(time.Time{})
  233. select {
  234. case p.pool <- client:
  235. // Returned to pool
  236. default:
  237. // Pool full, close connection
  238. client.Close()
  239. }
  240. }
  241. // Close closes all connections in the pool.
  242. func (p *KVPool) Close() error {
  243. p.mu.Lock()
  244. if p.closed {
  245. p.mu.Unlock()
  246. return nil
  247. }
  248. p.closed = true
  249. p.mu.Unlock()
  250. close(p.pool)
  251. for client := range p.pool {
  252. if client != nil {
  253. client.Close()
  254. }
  255. }
  256. return nil
  257. }
  258. // WithClient executes a function with a pooled connection.
  259. func (p *KVPool) WithClient(fn func(*KVClient) error) error {
  260. client, err := p.Get()
  261. if err != nil {
  262. return err
  263. }
  264. if err := fn(client); err != nil {
  265. if !isConnectionError(err) {
  266. p.Put(client)
  267. return err
  268. }
  269. // A timeout or short response can leave an acknowledgement buffered on
  270. // this connection. Never let the next request consume that response.
  271. client.Close()
  272. p.mu.Lock()
  273. if !p.closed {
  274. select {
  275. case p.pool <- nil:
  276. default:
  277. }
  278. }
  279. p.mu.Unlock()
  280. return err
  281. }
  282. p.Put(client)
  283. return nil
  284. }
  285. func isConnectionError(err error) bool {
  286. var netErr net.Error
  287. if errors.As(err, &netErr) {
  288. return true
  289. }
  290. message := err.Error()
  291. return strings.Contains(message, "write command failed:") ||
  292. strings.Contains(message, "flush failed:") ||
  293. strings.Contains(message, "read response failed:")
  294. }