2
0

kv.go 6.6 KB

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