Class: Narabikae::Position

Inherits:
Object
  • Object
show all
Defined in:
lib/narabikae/position.rb

Instance Method Summary collapse

Constructor Details

#initialize(record, option) ⇒ Position

Initializes a new instance of the Position class.

Parameters:

  • record (Object)

    Active Record object.

  • option (Option)


7
8
9
10
# File 'lib/narabikae/position.rb', line 7

def initialize(record, option)
  @record = record
  @option = option
end

Instance Method Details

#create_first_positionString

Generates a new key for the first position

Returns:

  • (String)

    The newly generated key for the first position.



22
23
24
# File 'lib/narabikae/position.rb', line 22

def create_first_position
  FractionalIndexer.generate_key(next_key: current_first_position)
end

#create_last_positionString

Generates a new key for the last position

Returns:

  • (String)

    The newly generated key for the last position.



15
16
17
# File 'lib/narabikae/position.rb', line 15

def create_last_position
  FractionalIndexer.generate_key(prev_key: current_last_position)
end

#find_position_after(target, challenge: 10) ⇒ String?

Finds the position after the specified target.

Uses an optimized neighbor-aware approach: queries the database for the next record's position and generates a key between the target and its neighbor. This avoids blind key generation and reduces collision retries.

Falls back to retry with random fractional for concurrent write race conditions.

Parameters:

  • target (ActiveRecord::Base, String)
  • challenge (Integer) (defaults to: 10)

    The number of times to attempt finding a valid position.

Returns:

  • (String, nil)

    The generated key for the position after the target, or nil if no valid position is found.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/narabikae/position.rb', line 37

def find_position_after(target, challenge: 10)
  # when target is nil, try to generate key from the last position
  target_key = extract_target_key(target) || current_last_position
  next_key = find_next_position_key(target_key)
  key = FractionalIndexer.generate_key(prev_key: target_key, next_key: next_key)
  return key if valid?(key)

  (challenge || 0).times do |i|
    key = FractionalIndexer.generate_key(prev_key: target_key, next_key: key)
    key += random_fractional
    return key if valid?(key)
  end

  nil
rescue FractionalIndexer::Error
  nil
end

#find_position_before(target, challenge: 10) ⇒ String?

Finds the position before the target position.

Uses an optimized neighbor-aware approach: queries the database for the previous record's position and generates a key between the neighbor and the target. This avoids blind key generation and reduces collision retries.

Falls back to retry with random fractional for concurrent write race conditions.

Parameters:

  • target (ActiveRecord::Base, String)
  • challenge (Integer) (defaults to: 10)

    The number of times to attempt finding a valid position.

Returns:

  • (String, nil)

    The generated key for the position before the target, or nil if no valid position is found.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/narabikae/position.rb', line 66

def find_position_before(target, challenge: 10)
  # when target is nil, try to generate key from the first position
  target_key = extract_target_key(target) || current_first_position
  prev_key = find_prev_position_key(target_key)
  key = FractionalIndexer.generate_key(prev_key: prev_key, next_key: target_key)
  return key if valid?(key)

  (challenge || 0).times do |i|
    key = FractionalIndexer.generate_key(prev_key: key, next_key: target_key)
    key += random_fractional
    return key if valid?(key)
  end

  nil
rescue FractionalIndexer::Error
  nil
end

#find_position_between(prev_target, next_target, challenge: 10) ⇒ string?

Finds the position between two targets.

Parameters:

  • prev_target (ActiveRecord::Base, String)

    The previous target.

  • next_target (ActiveRecord::Base, String)

    The next target.

  • challenge (Integer) (defaults to: 10)

    The number of times to attempt finding a valid position.

Returns:

  • (string, nil)

    The position between the two targets, or nil if no valid position is found.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/narabikae/position.rb', line 90

def find_position_between(prev_target, next_target, challenge: 10)
  prev_key = extract_target_key(prev_target)
  next_key = extract_target_key(next_target)
  return find_position_before(next_target, challenge: challenge) if prev_key.blank?
  return find_position_after(prev_target, challenge: challenge) if next_key.blank?

  prev_key, next_key = [ prev_key, next_key ].minmax
  key = FractionalIndexer.generate_key(
          prev_key: prev_key,
          next_key: next_key,
        )
  return key if valid?(key)

  (challenge || 0).times do |i|
    key = FractionalIndexer.generate_key(prev_key: key, next_key: next_key)
    key += random_fractional
    return key if valid?(key)
  end

  nil
rescue FractionalIndexer::Error
  nil
end