Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest to use hash index, instead of btree index in migration. #94

Closed
wants to merge 4 commits into from
Closed
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
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
name: Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby-version: [3.2.2]
database: [sqlite, postgres, mysql]
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ $ gem install solid_cache
Add the migration to your app:

```bash
$ bin/rails solid_cache:install:migrations
$ rake solid_cache:install
```

By default, this migration uses a btree index which descent through index to find a right entry. Hash index offers a faster record lookup, it’s O(1) operation that should improve lookup performance by 40-60%. But main drawback is that #delete_matched method will not be operational with hash index.

In case, you want to go with hash index run the following instead:

```bash
$ rake solid_cache:install[hash]
```

Then run it:
Expand Down
11 changes: 0 additions & 11 deletions db/migrate/20230724121448_create_solid_cache_entries.rb

This file was deleted.

18 changes: 14 additions & 4 deletions lib/generators/solid_cache/install/install_generator.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
require "rails/generators/active_record"

class SolidCache::InstallGenerator < Rails::Generators::Base
class_option :skip_migrations, type: :boolean, default: nil,
desc: "Skip migrations"
include ActiveRecord::Generators::Migration

source_root File.expand_path("templates", __dir__)
class_option :skip_migrations, type: :boolean, default: nil, desc: "Skip migrations"
class_option :index, type: :string, default: "btree", desc: "Index type for key column"

def add_rails_cache
%w[development test production].each do |env_name|
Expand All @@ -11,8 +16,13 @@ def add_rails_cache
end

def create_migrations
unless options[:skip_migrations]
rails_command "railties:install:migrations FROM=solid_cache", inline: true
return if options[:skip_migrations]

case options[:index]
when "btree"
migration_template "create_solid_cache_entries_btree.rb", "db/migrate/create_solid_cache_entries.rb"
when "hash"
migration_template "create_solid_cache_entries_hash.rb", "db/migrate/create_solid_cache_entries.rb"
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateSolidCacheEntries < ActiveRecord::Migration[7.0]
def change

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 4 spaces instead of 2 spaces

create_table :solid_cache_entries do |t|
t.binary :key, null: false, limit: 1024
t.binary :value, null: false, limit: 512.megabytes
t.datetime :created_at, null: false

t.index :key, unique: true
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateSolidCacheEntries < ActiveRecord::Migration[7.0]
def change

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

create_table :solid_cache_entries do |t|
t.binary :key, null: false, limit: 1024
t.binary :value, null: false, limit: 512.megabytes
t.datetime :created_at, null: false

t.index :key, unique: true, using: :hash

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error encountered with message
PG::FeatureNotSupported: ERROR: access method "hash" does not support unique indexes

From doc
https://www.postgresql.org/docs/16/indexes-unique.html
Currently, only B-tree indexes can be declared unique.

Copy link

@PikachuEXE PikachuEXE Jan 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I attempted to workaround it by removing unique: true, I would encounter another error
ArgumentError - No unique index found for key from
https://github.com/rails/rails/blob/v7.1.2/activerecord/lib/active_record/insert_all.rb#L161

Edit: And then I read comments above and it's already reported sorry~

end
end
end
5 changes: 5 additions & 0 deletions lib/solid_cache/store/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def delete_matched(matcher, options = {})
instrument :delete_matched, matcher do
raise ArgumentError, "Only strings are supported: #{matcher.inspect}" unless String === matcher
raise ArgumentError, "Strings cannot start with wildcards" if SQL_WILDCARD_CHARS.include?(matcher[0])
raise NotImplementedError, "Primary Key uses a Hash Index, delete_matched method is not supported in this case" if primary_key_using_hash_index?

options ||= {}
batch_size = options.fetch(:batch_size, 1000)
Expand Down Expand Up @@ -49,6 +50,10 @@ def clear(options = nil)
end

private
def primary_key_using_hash_index?
ActiveRecord::Base.connection.indexes(:activesupport_cache_entries).first { |i| i.columns.first == "key" }.using == :hash
end

def read_entry(key, **options)
deserialize_entry(read_serialized_entry(key, **options), **options)
end
Expand Down
10 changes: 8 additions & 2 deletions lib/tasks/solid_cache_tasks.rake
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
desc "Copy over the migration, and set cache"
namespace :solid_cache do
task :install do
Rails::Command.invoke :generate, [ "solid_cache:install" ]
task :install, [ :index ] do |_t, args|
args.with_defaults(index: "btree")

if args[:index] == "btree" || args[:index] == "hash"
Rails::Command.invoke :generate, [ "solid_cache:install", "--index=#{args[:index]}" ]
else
abort "Invalid index type - only 'btree' and 'hash' are supported."
end
end
end