solid_cache_mongoid 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +189 -0
- data/Rakefile +48 -0
- data/app/jobs/solid_cache_mongoid/expiry_job.rb +9 -0
- data/app/models/solid_cache_mongoid/entry/expiration.rb +55 -0
- data/app/models/solid_cache_mongoid/entry/size/estimate.rb +133 -0
- data/app/models/solid_cache_mongoid/entry/size/moving_average_estimate.rb +62 -0
- data/app/models/solid_cache_mongoid/entry/size.rb +21 -0
- data/app/models/solid_cache_mongoid/entry.rb +145 -0
- data/app/models/solid_cache_mongoid/record.rb +77 -0
- data/lib/active_support/cache/solid_cache_mongoid_store.rb +9 -0
- data/lib/generators/solid_cache/install/USAGE +9 -0
- data/lib/generators/solid_cache/install/install_generator.rb +20 -0
- data/lib/generators/solid_cache/install/templates/config/cache.yml.tt +20 -0
- data/lib/generators/solid_cache/install/templates/db/cache_schema.rb +12 -0
- data/lib/generators/solid_cache/install/templates/db/cache_structure.mysql.sql +56 -0
- data/lib/generators/solid_cache/install/templates/db/cache_structure.postgresql.sql +128 -0
- data/lib/generators/solid_cache/install/templates/db/cache_structure.sqlite3.sql +6 -0
- data/lib/solid_cache_mongoid/configuration.rb +31 -0
- data/lib/solid_cache_mongoid/connections/unmanaged.rb +33 -0
- data/lib/solid_cache_mongoid/connections.rb +9 -0
- data/lib/solid_cache_mongoid/engine.rb +38 -0
- data/lib/solid_cache_mongoid/store/api.rb +178 -0
- data/lib/solid_cache_mongoid/store/connections.rb +88 -0
- data/lib/solid_cache_mongoid/store/entries.rb +79 -0
- data/lib/solid_cache_mongoid/store/execution.rb +52 -0
- data/lib/solid_cache_mongoid/store/expiry.rb +49 -0
- data/lib/solid_cache_mongoid/store/failsafe.rb +39 -0
- data/lib/solid_cache_mongoid/store/stats.rb +30 -0
- data/lib/solid_cache_mongoid/store.rb +20 -0
- data/lib/solid_cache_mongoid/version.rb +5 -0
- data/lib/solid_cache_mongoid.rb +16 -0
- data/lib/tasks/solid_cache_tasks.rake +25 -0
- metadata +201 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidCacheMongoid
|
|
4
|
+
module Record
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
include Mongoid::Document
|
|
7
|
+
include Mongoid::Timestamps
|
|
8
|
+
include Mongoid::Locker
|
|
9
|
+
included do
|
|
10
|
+
NULL_INSTRUMENTER = ActiveSupport::Notifications::Instrumenter.new(ActiveSupport::Notifications::Fanout.new)
|
|
11
|
+
|
|
12
|
+
encrypt_with(key_id: ENV.fetch("SOLID_CACHE_KEY_ENCRYPT", nil) || Rails.application.secret_key_base) if SolidCacheMongoid.configuration.encrypt?
|
|
13
|
+
|
|
14
|
+
field :key, type: BSON::Binary, encrypt: SolidCacheMongoid.configuration.encryption_context_properties
|
|
15
|
+
field :value, type: BSON::Binary, encrypt: SolidCacheMongoid.configuration.encryption_context_properties
|
|
16
|
+
field :key_hash, type: Integer
|
|
17
|
+
field :byte_size, type: Integer
|
|
18
|
+
field :locking_name, type: String
|
|
19
|
+
field :locked_at, type: Time
|
|
20
|
+
|
|
21
|
+
index({ byte_size: 1 })
|
|
22
|
+
index({ key_hash: 1, byte_size: 1 })
|
|
23
|
+
index({ key_hash: 1 }, { unique: true })
|
|
24
|
+
|
|
25
|
+
store_in collection: SolidCacheMongoid.configuration.collection if SolidCacheMongoid.configuration.collection.present?
|
|
26
|
+
store_in client: SolidCacheMongoid.configuration.client if SolidCacheMongoid.configuration.client.present?
|
|
27
|
+
store_in database: SolidCacheMongoid.configuration.database if SolidCacheMongoid.configuration.database.present?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class_methods do
|
|
31
|
+
def disable_instrumentation(&block)
|
|
32
|
+
with_instrumenter(NULL_INSTRUMENTER, &block)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def with_instrumenter(instrumenter)
|
|
36
|
+
if ActiveSupport::Notifications.respond_to?(:instrumenter) && ActiveSupport::Notifications.respond_to?(:instrumenter=)
|
|
37
|
+
old = ActiveSupport::Notifications.instrumenter
|
|
38
|
+
ActiveSupport::Notifications.instrumenter = instrumenter
|
|
39
|
+
begin
|
|
40
|
+
yield
|
|
41
|
+
ensure
|
|
42
|
+
ActiveSupport::Notifications.instrumenter = old
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
# Fallback al comportamiento previo que usaba IsolatedExecutionState
|
|
46
|
+
old = ActiveSupport::IsolatedExecutionState[:active_record_instrumenter]
|
|
47
|
+
ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = instrumenter
|
|
48
|
+
begin
|
|
49
|
+
yield
|
|
50
|
+
ensure
|
|
51
|
+
ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = old
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def without_query_cache(&block)
|
|
57
|
+
Mongo::QueryCache.uncached(&block)
|
|
58
|
+
end
|
|
59
|
+
alias :uncached :without_query_cache
|
|
60
|
+
|
|
61
|
+
def with_query_cache(&block)
|
|
62
|
+
Mongo::QueryCache.cache(&block)
|
|
63
|
+
end
|
|
64
|
+
alias :cache :with_query_cache
|
|
65
|
+
|
|
66
|
+
def lease_connection
|
|
67
|
+
# Obtiene el cliente Mongo actual del modelo
|
|
68
|
+
client = self.mongo_client
|
|
69
|
+
|
|
70
|
+
# Asegura que hay una conexión disponible
|
|
71
|
+
client.reconnect unless client.cluster.connected?
|
|
72
|
+
|
|
73
|
+
yield client
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SolidCacheMongoid::InstallGenerator < Rails::Generators::Base
|
|
4
|
+
source_root File.expand_path("templates", __dir__)
|
|
5
|
+
|
|
6
|
+
def copy_files
|
|
7
|
+
template "config/cache.yml"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def configure_cache_store_adapter
|
|
11
|
+
gsub_file Pathname.new(destination_root).join("config/environments/production.rb"),
|
|
12
|
+
/(# )?config\.cache_store = (:.*)/, "config.cache_store = :solid_cache_mongoid_store"
|
|
13
|
+
|
|
14
|
+
gsub_file Pathname.new(destination_root).join("config/environments/development.rb"),
|
|
15
|
+
/(# )?config\.cache_store = (:.*)/, "config.cache_store = :solid_cache_mongoid_store"
|
|
16
|
+
|
|
17
|
+
gsub_file Pathname.new(destination_root).join("config/environments/test.rb"),
|
|
18
|
+
/(# )?config\.cache_store = (:.*)/, "config.cache_store = :solid_cache_mongoid_store"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
default: &default
|
|
2
|
+
# database: <%= ENV.fetch("SOLID_CACHE_DATABASE", "solid_cache") %>
|
|
3
|
+
# collection: solid_cache_entries
|
|
4
|
+
# client: default
|
|
5
|
+
# encrypt: false
|
|
6
|
+
store_options:
|
|
7
|
+
# Cap age of oldest cache entry to fulfill retention policies
|
|
8
|
+
# max_age: <%%= 60.days.to_i %>
|
|
9
|
+
max_size: <%%= 256.megabytes %>
|
|
10
|
+
namespace: <%%= Rails.env %>
|
|
11
|
+
|
|
12
|
+
development:
|
|
13
|
+
<<: *default
|
|
14
|
+
|
|
15
|
+
test:
|
|
16
|
+
<<: *default
|
|
17
|
+
|
|
18
|
+
production:
|
|
19
|
+
database: <%= ENV.fetch("SOLID_CACHE_DATABASE", "solid_cache") %>
|
|
20
|
+
<<: *default
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
ActiveRecord::Schema[7.2].define(version: 1) do
|
|
2
|
+
create_table "solid_cache_entries", force: :cascade do |t|
|
|
3
|
+
t.binary "key", limit: 1024, null: false
|
|
4
|
+
t.binary "value", limit: 536870912, null: false
|
|
5
|
+
t.datetime "created_at", null: false
|
|
6
|
+
t.integer "key_hash", limit: 8, null: false
|
|
7
|
+
t.integer "byte_size", limit: 4, null: false
|
|
8
|
+
t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size"
|
|
9
|
+
t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size"
|
|
10
|
+
t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
|
3
|
+
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
|
4
|
+
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
|
5
|
+
/*!50503 SET NAMES utf8mb4 */;
|
|
6
|
+
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
|
7
|
+
/*!40103 SET TIME_ZONE='+00:00' */;
|
|
8
|
+
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
|
9
|
+
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
|
10
|
+
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
|
11
|
+
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
|
12
|
+
DROP TABLE IF EXISTS `ar_internal_metadata`;
|
|
13
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
|
14
|
+
/*!50503 SET character_set_client = utf8mb4 */;
|
|
15
|
+
CREATE TABLE `ar_internal_metadata` (
|
|
16
|
+
`key` varchar(255) NOT NULL,
|
|
17
|
+
`value` varchar(255) DEFAULT NULL,
|
|
18
|
+
`created_at` datetime(6) NOT NULL,
|
|
19
|
+
`updated_at` datetime(6) NOT NULL,
|
|
20
|
+
PRIMARY KEY (`key`)
|
|
21
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
22
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
|
23
|
+
DROP TABLE IF EXISTS `schema_migrations`;
|
|
24
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
|
25
|
+
/*!50503 SET character_set_client = utf8mb4 */;
|
|
26
|
+
CREATE TABLE `schema_migrations` (
|
|
27
|
+
`version` varchar(255) NOT NULL,
|
|
28
|
+
PRIMARY KEY (`version`)
|
|
29
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
30
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
|
31
|
+
DROP TABLE IF EXISTS `solid_cache_entries`;
|
|
32
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
|
33
|
+
/*!50503 SET character_set_client = utf8mb4 */;
|
|
34
|
+
CREATE TABLE `solid_cache_entries` (
|
|
35
|
+
`id` bigint NOT NULL AUTO_INCREMENT,
|
|
36
|
+
`key` varbinary(1024) NOT NULL,
|
|
37
|
+
`value` longblob NOT NULL,
|
|
38
|
+
`created_at` datetime(6) NOT NULL,
|
|
39
|
+
`key_hash` bigint NOT NULL,
|
|
40
|
+
`byte_size` int NOT NULL,
|
|
41
|
+
PRIMARY KEY (`id`),
|
|
42
|
+
UNIQUE KEY `index_solid_cache_entries_on_key_hash` (`key_hash`),
|
|
43
|
+
KEY `index_solid_cache_entries_on_byte_size` (`byte_size`),
|
|
44
|
+
KEY `index_solid_cache_entries_on_key_hash_and_byte_size` (`key_hash`,`byte_size`)
|
|
45
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
46
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
|
47
|
+
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
|
48
|
+
|
|
49
|
+
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
|
50
|
+
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
|
51
|
+
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
|
52
|
+
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
|
53
|
+
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
|
54
|
+
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
|
55
|
+
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
|
56
|
+
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
SET statement_timeout = 0;
|
|
2
|
+
SET lock_timeout = 0;
|
|
3
|
+
SET idle_in_transaction_session_timeout = 0;
|
|
4
|
+
SET transaction_timeout = 0;
|
|
5
|
+
SET client_encoding = 'UTF8';
|
|
6
|
+
SET standard_conforming_strings = on;
|
|
7
|
+
SELECT pg_catalog.set_config('search_path', '', false);
|
|
8
|
+
SET check_function_bodies = false;
|
|
9
|
+
SET xmloption = content;
|
|
10
|
+
SET client_min_messages = warning;
|
|
11
|
+
SET row_security = off;
|
|
12
|
+
|
|
13
|
+
SET default_tablespace = '';
|
|
14
|
+
|
|
15
|
+
SET default_table_access_method = heap;
|
|
16
|
+
|
|
17
|
+
--
|
|
18
|
+
-- Name: ar_internal_metadata; Type: TABLE; Schema: public; Owner: -
|
|
19
|
+
--
|
|
20
|
+
|
|
21
|
+
CREATE TABLE public.ar_internal_metadata (
|
|
22
|
+
key character varying NOT NULL,
|
|
23
|
+
value character varying,
|
|
24
|
+
created_at timestamp(6) without time zone NOT NULL,
|
|
25
|
+
updated_at timestamp(6) without time zone NOT NULL
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
--
|
|
30
|
+
-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: -
|
|
31
|
+
--
|
|
32
|
+
|
|
33
|
+
CREATE TABLE public.schema_migrations (
|
|
34
|
+
version character varying NOT NULL
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
--
|
|
39
|
+
-- Name: solid_cache_entries; Type: TABLE; Schema: public; Owner: -
|
|
40
|
+
--
|
|
41
|
+
|
|
42
|
+
CREATE TABLE public.solid_cache_entries (
|
|
43
|
+
id bigint NOT NULL,
|
|
44
|
+
key bytea NOT NULL,
|
|
45
|
+
value bytea NOT NULL,
|
|
46
|
+
created_at timestamp(6) without time zone NOT NULL,
|
|
47
|
+
key_hash bigint NOT NULL,
|
|
48
|
+
byte_size integer NOT NULL
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
--
|
|
53
|
+
-- Name: solid_cache_entries_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
|
54
|
+
--
|
|
55
|
+
|
|
56
|
+
CREATE SEQUENCE public.solid_cache_entries_id_seq
|
|
57
|
+
START WITH 1
|
|
58
|
+
INCREMENT BY 1
|
|
59
|
+
NO MINVALUE
|
|
60
|
+
NO MAXVALUE
|
|
61
|
+
CACHE 1;
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
--
|
|
65
|
+
-- Name: solid_cache_entries_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
|
66
|
+
--
|
|
67
|
+
|
|
68
|
+
ALTER SEQUENCE public.solid_cache_entries_id_seq OWNED BY public.solid_cache_entries.id;
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
--
|
|
72
|
+
-- Name: solid_cache_entries id; Type: DEFAULT; Schema: public; Owner: -
|
|
73
|
+
--
|
|
74
|
+
|
|
75
|
+
ALTER TABLE ONLY public.solid_cache_entries ALTER COLUMN id SET DEFAULT nextval('public.solid_cache_entries_id_seq'::regclass);
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
--
|
|
79
|
+
-- Name: ar_internal_metadata ar_internal_metadata_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
|
80
|
+
--
|
|
81
|
+
|
|
82
|
+
ALTER TABLE ONLY public.ar_internal_metadata
|
|
83
|
+
ADD CONSTRAINT ar_internal_metadata_pkey PRIMARY KEY (key);
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
--
|
|
87
|
+
-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
|
88
|
+
--
|
|
89
|
+
|
|
90
|
+
ALTER TABLE ONLY public.schema_migrations
|
|
91
|
+
ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
--
|
|
95
|
+
-- Name: solid_cache_entries solid_cache_entries_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
|
96
|
+
--
|
|
97
|
+
|
|
98
|
+
ALTER TABLE ONLY public.solid_cache_entries
|
|
99
|
+
ADD CONSTRAINT solid_cache_entries_pkey PRIMARY KEY (id);
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
--
|
|
103
|
+
-- Name: index_solid_cache_entries_on_byte_size; Type: INDEX; Schema: public; Owner: -
|
|
104
|
+
--
|
|
105
|
+
|
|
106
|
+
CREATE INDEX index_solid_cache_entries_on_byte_size ON public.solid_cache_entries USING btree (byte_size);
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
--
|
|
110
|
+
-- Name: index_solid_cache_entries_on_key_hash; Type: INDEX; Schema: public; Owner: -
|
|
111
|
+
--
|
|
112
|
+
|
|
113
|
+
CREATE UNIQUE INDEX index_solid_cache_entries_on_key_hash ON public.solid_cache_entries USING btree (key_hash);
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
--
|
|
117
|
+
-- Name: index_solid_cache_entries_on_key_hash_and_byte_size; Type: INDEX; Schema: public; Owner: -
|
|
118
|
+
--
|
|
119
|
+
|
|
120
|
+
CREATE INDEX index_solid_cache_entries_on_key_hash_and_byte_size ON public.solid_cache_entries USING btree (key_hash, byte_size);
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
--
|
|
124
|
+
-- PostgreSQL database dump complete
|
|
125
|
+
--
|
|
126
|
+
|
|
127
|
+
SET search_path TO "$user", public;
|
|
128
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
|
|
2
|
+
CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
|
|
3
|
+
CREATE TABLE IF NOT EXISTS "solid_cache_entries" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "key" blob(1024) NOT NULL, "value" blob(536870912) NOT NULL, "created_at" datetime(6) NOT NULL, "key_hash" integer(8) NOT NULL, "byte_size" integer(4) NOT NULL);
|
|
4
|
+
CREATE INDEX "index_solid_cache_entries_on_byte_size" ON "solid_cache_entries" ("byte_size");
|
|
5
|
+
CREATE INDEX "index_solid_cache_entries_on_key_hash_and_byte_size" ON "solid_cache_entries" ("key_hash", "byte_size");
|
|
6
|
+
CREATE UNIQUE INDEX "index_solid_cache_entries_on_key_hash" ON "solid_cache_entries" ("key_hash");
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidCacheMongoid
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_reader :store_options, :database, :client, :collection, :executor, :size_estimate_samples, :encrypt, :encryption_context_properties
|
|
6
|
+
|
|
7
|
+
def initialize(
|
|
8
|
+
store_options: {}, database: nil, collection: nil, client: nil,
|
|
9
|
+
executor: nil, encrypt: false, encryption_context_properties: nil, size_estimate_samples: 10_000
|
|
10
|
+
)
|
|
11
|
+
@store_options = store_options
|
|
12
|
+
@size_estimate_samples = size_estimate_samples
|
|
13
|
+
@executor = executor
|
|
14
|
+
@encrypt = encrypt
|
|
15
|
+
@client = client
|
|
16
|
+
@collection = collection || "solid_cache_entries"
|
|
17
|
+
@database = database || "solid_cache_mongoid"
|
|
18
|
+
@encryption_context_properties = encryption_context_properties
|
|
19
|
+
@encryption_context_properties ||= default_encryption_context_properties if encrypt?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def encrypt?
|
|
23
|
+
encrypt.present?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
def default_encryption_context_properties
|
|
28
|
+
{ deterministic: false }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidCacheMongoid
|
|
4
|
+
module Connections
|
|
5
|
+
class Unmanaged
|
|
6
|
+
def with_each
|
|
7
|
+
return enum_for(:with_each) unless block_given?
|
|
8
|
+
|
|
9
|
+
yield
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def with(name)
|
|
13
|
+
yield
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def with_connection_for(key)
|
|
17
|
+
yield
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def assign(keys)
|
|
21
|
+
{ default: keys }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def count
|
|
25
|
+
1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def names
|
|
29
|
+
[ :default ]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support"
|
|
4
|
+
require "mongoid"
|
|
5
|
+
|
|
6
|
+
module SolidCacheMongoid
|
|
7
|
+
class Engine < ::Rails::Engine
|
|
8
|
+
isolate_namespace SolidCacheMongoid
|
|
9
|
+
|
|
10
|
+
config.solid_cache = ActiveSupport::OrderedOptions.new
|
|
11
|
+
|
|
12
|
+
initializer "solid_cache_mongoid.config", before: :initialize_cache do |app|
|
|
13
|
+
config_paths = %w[config/cache config/solid_cache_mongoid]
|
|
14
|
+
|
|
15
|
+
config_paths.each do |path|
|
|
16
|
+
app.paths.add path, with: ENV["SOLID_CACHE_CONFIG"] || "#{path}.yml"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
config_pathname = config_paths.map { |path| Pathname.new(app.config.paths[path].first) }.find(&:exist?)
|
|
20
|
+
|
|
21
|
+
options = config_pathname ? app.config_for(config_pathname).to_h.deep_symbolize_keys : {}
|
|
22
|
+
|
|
23
|
+
options[:size_estimate_samples] = config.solid_cache.size_estimate_samples if config.solid_cache.size_estimate_samples
|
|
24
|
+
options[:encrypt] = config.solid_cache.encrypt if config.solid_cache.encrypt
|
|
25
|
+
options[:encryption_context_properties] = config.solid_cache.encryption_context_properties if config.solid_cache.encryption_context_properties
|
|
26
|
+
|
|
27
|
+
SolidCacheMongoid.configuration = SolidCacheMongoid::Configuration.new(**options)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
initializer "solid_cache_mongoid.app_executor", before: :run_prepare_callbacks do |app|
|
|
31
|
+
SolidCacheMongoid.executor = config.solid_cache.executor || app.executor
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
config.after_initialize do
|
|
35
|
+
Rails.cache.setup! if Rails.cache.is_a?(Store)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidCacheMongoid
|
|
4
|
+
class Store
|
|
5
|
+
module Api
|
|
6
|
+
DEFAULT_MAX_KEY_BYTESIZE = 1024
|
|
7
|
+
|
|
8
|
+
attr_reader :max_key_bytesize
|
|
9
|
+
|
|
10
|
+
def initialize(options = {})
|
|
11
|
+
super(options)
|
|
12
|
+
|
|
13
|
+
@max_key_bytesize = options.fetch(:max_key_bytesize, DEFAULT_MAX_KEY_BYTESIZE)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def increment(name, amount = 1, options = nil)
|
|
17
|
+
options = merged_options(options)
|
|
18
|
+
key = normalize_key(name, options)
|
|
19
|
+
|
|
20
|
+
instrument :increment, key, amount: amount do
|
|
21
|
+
adjust(name, amount, options)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def decrement(name, amount = 1, options = nil)
|
|
26
|
+
options = merged_options(options)
|
|
27
|
+
key = normalize_key(name, options)
|
|
28
|
+
|
|
29
|
+
instrument :decrement, key, amount: amount do
|
|
30
|
+
adjust(name, -amount, options)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def cleanup(options = nil)
|
|
35
|
+
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def clear(options = nil)
|
|
39
|
+
entry_clear
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
def read_entry(key, **options)
|
|
44
|
+
deserialize_entry(read_serialized_entry(key, **options), **options)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def read_serialized_entry(key, **options)
|
|
48
|
+
entry_read(key)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def write_entry(key, entry, raw: false, unless_exist: false, **options)
|
|
52
|
+
payload = serialize_entry(entry, raw: raw, **options)
|
|
53
|
+
|
|
54
|
+
if unless_exist
|
|
55
|
+
written = false
|
|
56
|
+
entry_lock_and_write(key) do |value|
|
|
57
|
+
if value.nil? || deserialize_entry(value, **options).expired?
|
|
58
|
+
written = true
|
|
59
|
+
payload
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
written = entry_write(key, payload)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
write_serialized_entry(key, payload, raw: raw, returning: written, **options)
|
|
67
|
+
written
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, returning: true, **options)
|
|
71
|
+
returning
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def read_serialized_entries(keys)
|
|
75
|
+
entry_read_multi(keys).reduce(&:merge!)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def read_multi_entries(names, **options)
|
|
79
|
+
keys_and_names = names.index_by { |name| normalize_key(name, options) }
|
|
80
|
+
serialized_entries = read_serialized_entries(keys_and_names.keys)
|
|
81
|
+
|
|
82
|
+
keys_and_names.each_with_object({}) do |(key, name), results|
|
|
83
|
+
serialized_entry = serialized_entries[key]
|
|
84
|
+
entry = deserialize_entry(serialized_entry, **options)
|
|
85
|
+
|
|
86
|
+
next unless entry
|
|
87
|
+
|
|
88
|
+
version = normalize_version(name, options)
|
|
89
|
+
|
|
90
|
+
if entry.expired?
|
|
91
|
+
delete_entry(key, **options)
|
|
92
|
+
elsif !entry.mismatched?(version)
|
|
93
|
+
if defined? ActiveSupport::Cache::DeserializationError
|
|
94
|
+
begin
|
|
95
|
+
results[name] = entry.value
|
|
96
|
+
rescue ActiveSupport::Cache::DeserializationError
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
results[name] = entry.value
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def write_multi_entries(entries, expires_in: nil, **options)
|
|
106
|
+
if entries.any?
|
|
107
|
+
serialized_entries = serialize_entries(entries, **options)
|
|
108
|
+
# to add them to the local cache
|
|
109
|
+
serialized_entries.each do |entries|
|
|
110
|
+
write_serialized_entry(entries[:key], entries[:value])
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
entry_write_multi(serialized_entries).all?
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def delete_entry(key, **options)
|
|
118
|
+
entry_delete(key)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def delete_multi_entries(entries, **options)
|
|
122
|
+
entry_delete_multi(entries).compact.sum
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def serialize_entry(entry, raw: false, **options)
|
|
126
|
+
super(entry, raw: raw, **options)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def serialize_entries(entries, **options)
|
|
130
|
+
entries.map do |key, entry|
|
|
131
|
+
{ key: key, value: serialize_entry(entry, **options) }
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def deserialize_entry(payload, **)
|
|
136
|
+
super(payload)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def normalize_key(key, options)
|
|
140
|
+
truncate_key super&.b
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def truncate_key(key)
|
|
144
|
+
if key && key.bytesize > max_key_bytesize
|
|
145
|
+
suffix = ":hash:#{ActiveSupport::Digest.hexdigest(key)}"
|
|
146
|
+
truncate_at = max_key_bytesize - suffix.bytesize
|
|
147
|
+
"#{key.byteslice(0, truncate_at)}#{suffix}".b
|
|
148
|
+
else
|
|
149
|
+
key
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def adjust(name, amount, options)
|
|
154
|
+
options = merged_options(options)
|
|
155
|
+
key = normalize_key(name, options)
|
|
156
|
+
|
|
157
|
+
new_value = entry_lock_and_write(key) do |value|
|
|
158
|
+
serialize_entry(adjusted_entry(value, amount, options))
|
|
159
|
+
end
|
|
160
|
+
deserialize_entry(new_value, **options).value if new_value
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def adjusted_entry(value, amount, options)
|
|
164
|
+
entry = deserialize_entry(value, **options)
|
|
165
|
+
|
|
166
|
+
if entry && !entry.expired?
|
|
167
|
+
ActiveSupport::Cache::Entry.new \
|
|
168
|
+
amount + entry.value.to_i, **options.dup.merge(expires_in: nil, expires_at: entry.expires_at)
|
|
169
|
+
elsif /\A\d+\z/.match?(value)
|
|
170
|
+
# This is to match old raw values
|
|
171
|
+
ActiveSupport::Cache::Entry.new(amount + value.to_i, **options)
|
|
172
|
+
else
|
|
173
|
+
ActiveSupport::Cache::Entry.new(amount, **options)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|