panda_pal 5.16.1 → 5.16.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b33c2970defb1a7405a8482186720ab63a05b10db522e4c39ce56b9aa38c1b9
4
- data.tar.gz: 6a44dc5db6d135882e4c2aefe8b7872ea87ccd066fe329b0764e2f8564694800
3
+ metadata.gz: a2c0409f3edd41806fcd606b52eb0c6441c43dac7b9e1eeb89c1871b6e08525f
4
+ data.tar.gz: 14b3313348391ab1c364ee9bb404af319bf93b831fc5bd552059faf41a179b71
5
5
  SHA512:
6
- metadata.gz: 589b2581d892bca49ba635d01a70b9db1f6044b9c885758fdbfb6d483ca3c91861bc22515fdbde2086499c7d380b96f199f8dd26e44e1cd900f2a3c3c72fc53a
7
- data.tar.gz: 330d2c17013313a47f361f12709acd99291fe918cdea00405b7f76d55925557000c66b0eee08ea68049ece4acd77e86db1fd3acec73ccacaa3165e235cadcafa
6
+ metadata.gz: e2c460dd7ac5ff05da55ef6db233f0d266983927e6a295b469d1d19002ea833b26479c5b7db53fb6306761afc683bf3494fc07c63f1ac401f736d73ad803248b
7
+ data.tar.gz: bcccc2e5ad50d546f3258e513118e97a37f8104901436b3b3920d64986ad83159034a905cce7ded7b81529989d16d8151f0583ec7b273732e2d38be443a6dc8e
@@ -250,49 +250,71 @@ module PandaPal::Plugins::ApartmentCache
250
250
  end
251
251
  ActiveSupport::Cache::Store.send(:prepend, PandaPal::Plugins::ApartmentCache)
252
252
 
253
- if defined?(ActionCable)
254
- module ActionCable
255
- module Channel
256
- class Base
257
- def self.broadcasting_for(model)
258
- # Rails 5 #stream_for passes #channel_name as part of model. Rails 6 doesn't and includes it via #broadcasting_for.
259
- model = [channel_name, model] unless model.is_a?(Array)
260
- serialize_broadcasting([ Apartment::Tenant.current, model ])
261
- end
253
+ ActiveSupport.on_load(:action_cable) do
254
+ ActionCable::Connection::Base.module_eval do
255
+ def tenant=(name)
256
+ @tenant = name
257
+ end
258
+
259
+ def tenant
260
+ @tenant || 'public'
261
+ end
262
+ end
262
263
 
263
- def self.serialize_broadcasting(object)
264
- case
265
- when object.is_a?(Array)
266
- object.map { |m| serialize_broadcasting(m) }.join(":")
267
- when object.respond_to?(:to_gid_param)
268
- object.to_gid_param
269
- else
270
- object.to_param
264
+ # Include the Current Tenant in any broadcastings
265
+ if Rails.version >= '6.0'
266
+ ActionCable::Channel::Base.define_singleton_method(:broadcasting_for) do |*args|
267
+ cconn = ActionCable.server.worker_pool.connection
268
+ items = [cconn&.tenant || Apartment::Tenant.current, channel_name, *args]
269
+ serialize_broadcasting(items)
270
+ end
271
+ else
272
+ module ActionCable
273
+ module Channel
274
+ class Base
275
+ def broadcasting_for(model)
276
+ super([ Apartment::Tenant.current, model ])
271
277
  end
272
278
  end
273
279
  end
274
280
  end
275
281
  end
276
282
 
277
- module PandaPal::Plugins::ActionCableApartment
278
- module Connection
279
- def tenant=(name)
280
- @tenant = name
281
- end
283
+ # Lazily switch any worker threads to the correct tenant when they are working
284
+ # Actively calling `switch_tenant` for checks out a DB connection and calls `SET search_path`.
285
+ # The message processing may not interface with the DB, so this would be a huge waste.
286
+ # Instead, we ensure that the thread will trigger a :checkout if it needs a connection,
287
+ # at which time we hack-in the correct tenant/schema.
288
+
289
+ ActionCable::Server::Worker.set_callback :work, :around do |_, blk|
290
+ # Bit of a hack, but ensures the adapter is initialized (since such may incur DB calls (requiring a connection)
291
+ # which would then cause `checkout` to recurse)
292
+ Apartment::Tenant.adapter
293
+
294
+ # If the current thread already has a connection checked out, release it back to the pool so that the later after_checkout
295
+ # callback can set the schema properly.
296
+ pool = Apartment.connection_class.connection_pool
297
+ pool.release_connection if pool.active_connection?
298
+
299
+ Thread.current[:cable_tenant] = connection.tenant
300
+ blk.call
301
+ ensure
302
+ Thread.current[:cable_tenant] = nil
303
+ end
282
304
 
283
- def tenant
284
- @tenant || 'public'
285
- end
305
+ ActiveSupport.on_load(:active_record) do
306
+ if Apartment::Tenant.adapter.is_a?(Apartment::Adapters::PostgresqlSchemaAdapter)
307
+ ActiveRecord::Base.connection.class.set_callback :checkout, :after do |conn|
308
+ next unless conn.pool == Apartment.connection_class.connection_pool
286
309
 
287
- def dispatch_websocket_message(*args, **kwargs)
288
- Apartment::Tenant.switch(tenant) do
289
- super
310
+ if (ct = Thread.current[:cable_tenant]).present?
311
+ adapter = Apartment::Tenant.adapter
312
+ adapter.instance_variable_set(:@current, ct)
313
+ conn.schema_search_path = adapter.send :full_search_path
290
314
  end
291
315
  end
292
316
  end
293
317
  end
294
-
295
- ActionCable::Connection::Base.prepend(PandaPal::Plugins::ActionCableApartment::Connection)
296
318
  end
297
319
 
298
320
  if defined?(Delayed)
@@ -1,3 +1,3 @@
1
1
  module PandaPal
2
- VERSION = "5.16.1"
2
+ VERSION = "5.16.3"
3
3
  end
@@ -31,6 +31,28 @@ RSpec.describe PandaPal::Organization, type: :model do
31
31
  end
32
32
  end
33
33
 
34
+ it "creates records on the target shard and schema" do
35
+ org1.switch_tenant
36
+ PandaPal::ApiCall.create!(logic: "from_primary")
37
+ expect(PandaPal::ApiCall.count).to eq(1)
38
+ expect(PandaPal::ApiCall.first.logic).to eq("from_primary")
39
+
40
+ org2.switch_tenant
41
+ PandaPal::ApiCall.create!(logic: "from_alt")
42
+ expect(PandaPal::ApiCall.count).to eq(1)
43
+ expect(PandaPal::ApiCall.first.logic).to eq("from_alt")
44
+
45
+ org1.switch_tenant
46
+ expect(PandaPal::ApiCall.count).to eq(1)
47
+ expect(PandaPal::ApiCall.first.logic).to eq("from_primary")
48
+ expect(PandaPal::ApiCall.connection.exec_query("SELECT current_database()").pluck("current_database")[0]).to eq("panda_pal_test1")
49
+
50
+ org2.switch_tenant
51
+ expect(PandaPal::ApiCall.count).to eq(1)
52
+ expect(PandaPal::ApiCall.first.logic).to eq("from_alt")
53
+ expect(PandaPal::ApiCall.connection.exec_query("SELECT current_database()").pluck("current_database")[0]).to eq("panda_pal_test2")
54
+ end
55
+
34
56
  context "load_async" do
35
57
  it "works across shards" do
36
58
  qs = []
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: panda_pal
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.16.1
4
+ version: 5.16.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Instructure CustomDev