Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/typesense-rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ class << base
end

def collection_name_with_timestamp(options)
"#{typesense_collection_name(options)}_#{Time.now.to_i}"
"#{typesense_collection_name(options)}_#{Time.now.to_i}_#{SecureRandom.hex(4)}"
end

def typesense_create_collection(collection_name, settings = nil, existing_collection: nil)
Expand Down Expand Up @@ -635,10 +635,11 @@ def typesense_reindex(batch_size = Typesense::IndexSettings::DEFAULT_BATCH_SIZE)
next if typesense_indexing_disabled?(options)

existing_collection_resources = {}
old_collection_name = nil
begin
master_index = typesense_ensure_init(options, settings, false)
existing_collection_resources = typesense_collection_resources(master_index[:alias_name])
delete_collection(master_index[:alias_name])
old_collection_name = master_index[:collection_name]
rescue ArgumentError
@typesense_indexes[settings] = { collection_name: "", alias_name: typesense_collection_name(options) }
master_index = @typesense_indexes[settings]
Expand All @@ -665,6 +666,7 @@ def typesense_reindex(batch_size = Typesense::IndexSettings::DEFAULT_BATCH_SIZE)
end

upsert_alias(src_index_name, master_index[:alias_name])
delete_collection(old_collection_name) if old_collection_name.present? && old_collection_name != src_index_name
master_index[:collection_name] = src_index_name
end
nil
Expand Down
75 changes: 75 additions & 0 deletions spec/integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@
create_table :misconfigured_blocks do |t|
t.string :name
end
create_table :reindex_alias_probes do |t|
t.string :name
end
if defined?(ActiveModel::Serializer)
create_table :serialized_objects do |t|
t.string :name
Expand Down Expand Up @@ -248,6 +251,14 @@ def self.truth
end
end

class ReindexAliasProbe < ActiveRecord::Base
include Typesense

typesense auto_index: false, index_name: safe_index_name("ReindexAliasProbe") do
attribute :name
end
end

module Namespaced
def self.table_name_prefix
"namespaced_"
Expand Down Expand Up @@ -991,6 +1002,33 @@ class SerializedObject < ActiveRecord::Base
expect(Product.search("*", "", { "per_page" => Typesense::IndexSettings::DEFAULT_BATCH_SIZE }).size).to eq(n)
end

it "does not delete the live alias target before swapping during reindex" do
alias_name = Product.index_name
old_collection_name = "#{alias_name}_old"
new_collection_name = "#{alias_name}_new"
previous_indexes = Product.instance_variable_get(:@typesense_indexes)

Product.instance_variable_set(:@typesense_indexes, {})

allow(Product).to receive(:get_collection).with(alias_name).and_return({ "name" => old_collection_name })
allow(Product).to receive(:typesense_collection_resources).with(alias_name).and_return({})
allow(Product).to receive(:collection_name_with_timestamp).and_return(new_collection_name)
allow(Product).to receive(:typesense_find_in_batches)

expect(Product).not_to receive(:delete_collection).with(alias_name)
expect(Product).to receive(:create_collection).with(
new_collection_name,
Product.typesense_settings,
existing_collection: {}
).ordered
expect(Product).to receive(:upsert_alias).with(new_collection_name, alias_name).ordered
expect(Product).to receive(:delete_collection).with(old_collection_name).ordered

Product.reindex(Typesense::IndexSettings::DEFAULT_BATCH_SIZE)
ensure
Product.instance_variable_set(:@typesense_indexes, previous_indexes)
end

it "should not return products that are not indexable" do
@sekrit.index!
@no_href.index!
Expand Down Expand Up @@ -1071,6 +1109,43 @@ class SerializedObject < ActiveRecord::Base
end
end

describe "ReindexAliasProbe" do
before(:each) do
ReindexAliasProbe.delete_all
ReindexAliasProbe.clear_index!
rescue StandardError
ArgumentError
ensure
ReindexAliasProbe.create!(name: "alpha")
ReindexAliasProbe.create!(name: "beta")
ReindexAliasProbe.reindex(Typesense::IndexSettings::DEFAULT_BATCH_SIZE)
end

after(:each) do
ReindexAliasProbe.delete_all
ReindexAliasProbe.clear_index!
rescue StandardError
ArgumentError
end

it "keeps the alias searchable while reindex builds the replacement collection" do
alias_name = ReindexAliasProbe.index_name
search_params = { q: "alpha", query_by: "name" }

expect(ReindexAliasProbe.typesense_client.collections[alias_name].documents.search(search_params)["found"]).to eq(1)

expect(ReindexAliasProbe).to receive(:create_collection).and_wrap_original do |original, *args, **kwargs|
expect(
ReindexAliasProbe.typesense_client.collections[alias_name].documents.search(search_params)["found"]
).to eq(1)
original.call(*args, **kwargs)
end

ReindexAliasProbe.reindex(Typesense::IndexSettings::DEFAULT_BATCH_SIZE)
expect(ReindexAliasProbe.typesense_client.collections[alias_name].documents.search(search_params)["found"]).to eq(1)
end
end

describe "Book" do
before(:all) do
Book.clear_index!
Expand Down
Loading