File: response_buffer.rb

package info (click to toggle)
ruby-dalli 5.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 992 kB
  • sloc: ruby: 9,447; sh: 19; makefile: 4
file content (78 lines) | stat: -rw-r--r-- 2,162 bytes parent folder | download
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
# frozen_string_literal: true

require 'socket'
require 'timeout'

module Dalli
  module Protocol
    ##
    # Manages the buffer for responses from memcached.
    # Uses an offset-based approach to avoid string allocations
    # when advancing through parsed responses.
    ##
    class ResponseBuffer
      # Compact the buffer when the consumed portion exceeds this
      # threshold and represents more than half the buffer
      COMPACT_THRESHOLD = 4096

      def initialize(io_source, response_processor)
        @io_source = io_source
        @response_processor = response_processor
        @buffer = nil
        @offset = 0
      end

      def read
        @buffer << @io_source.read_nonblock
      end

      # Attempts to process a single response from the buffer,
      # advancing the offset past the consumed bytes.
      def process_single_getk_response
        bytes, status, cas, key, value = @response_processor.getk_response_from_buffer(@buffer, @offset)
        @offset += bytes
        compact_if_needed
        [status, cas, key, value]
      end

      # Resets the internal buffer to an empty state,
      # so that we're ready to read pipelined responses
      def reset
        @buffer = ''.b
        @offset = 0
      end

      # Ensures the buffer is initialized for reading without discarding
      # existing data. Used by interleaved pipelined get which may have
      # already buffered partial responses during the send phase.
      def ensure_ready
        return if in_progress?

        @buffer = ''.b
        @offset = 0
      end

      # Clear the internal response buffer
      def clear
        @buffer = nil
        @offset = 0
      end

      def in_progress?
        !@buffer.nil?
      end

      private

      # Only compact when we've consumed a significant portion of the buffer.
      # This avoids per-response string allocation while preventing unbounded
      # memory growth for large pipelines.
      def compact_if_needed
        return unless @offset > COMPACT_THRESHOLD && @offset > @buffer.bytesize / 2

        @buffer = @buffer.byteslice(@offset..)
        @offset = 0
      end
    end
  end
end