message_bus 3.3.1 → 3.3.6
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.
Potentially problematic release.
This version of message_bus might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/.eslintrc.js +13 -0
 - data/.github/workflows/ci.yml +54 -0
 - data/.gitignore +2 -0
 - data/.rubocop.yml +3 -1
 - data/CHANGELOG +47 -0
 - data/DEV.md +9 -0
 - data/Gemfile +2 -0
 - data/README.md +30 -6
 - data/Rakefile +1 -1
 - data/assets/message-bus-ajax.js +1 -7
 - data/assets/message-bus.js +72 -76
 - data/bench/codecs/all_codecs.rb +39 -0
 - data/bench/codecs/marshal.rb +11 -0
 - data/bench/codecs/packed_string.rb +67 -0
 - data/bench/codecs/string_hack.rb +47 -0
 - data/bench/codecs_large_user_list.rb +29 -0
 - data/bench/codecs_standard_message.rb +29 -0
 - data/lib/message_bus.rb +51 -24
 - data/lib/message_bus/backends/base.rb +0 -2
 - data/lib/message_bus/backends/memory.rb +0 -2
 - data/lib/message_bus/backends/postgres.rb +0 -2
 - data/lib/message_bus/backends/redis.rb +3 -5
 - data/lib/message_bus/client.rb +0 -1
 - data/lib/message_bus/codec/base.rb +18 -0
 - data/lib/message_bus/codec/json.rb +15 -0
 - data/lib/message_bus/codec/oj.rb +21 -0
 - data/lib/message_bus/distributed_cache.rb +7 -2
 - data/lib/message_bus/message.rb +2 -2
 - data/lib/message_bus/rack/middleware.rb +16 -16
 - data/lib/message_bus/timer_thread.rb +8 -1
 - data/lib/message_bus/version.rb +1 -1
 - data/message_bus.gemspec +1 -1
 - data/package-lock.json +2192 -0
 - data/package.json +8 -3
 - data/spec/lib/message_bus_spec.rb +34 -5
 - metadata +20 -9
 - data/.travis.yml +0 -17
 - data/lib/message_bus/em_ext.rb +0 -6
 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative './packed_string'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative './string_hack'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative './marshal'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            def all_codecs
         
     | 
| 
      
 8 
     | 
    
         
            +
              {
         
     | 
| 
      
 9 
     | 
    
         
            +
                json: MessageBus::Codec::Json.new,
         
     | 
| 
      
 10 
     | 
    
         
            +
                oj: MessageBus::Codec::Oj.new,
         
     | 
| 
      
 11 
     | 
    
         
            +
                marshal: MarshalCodec.new,
         
     | 
| 
      
 12 
     | 
    
         
            +
                packed_string_4_bytes: PackedString.new("V"),
         
     | 
| 
      
 13 
     | 
    
         
            +
                packed_string_8_bytes: PackedString.new("Q"),
         
     | 
| 
      
 14 
     | 
    
         
            +
                string_hack: StringHack.new
         
     | 
| 
      
 15 
     | 
    
         
            +
              }
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            def bench_decode(hash, user_needle)
         
     | 
| 
      
 19 
     | 
    
         
            +
              encoded_data = all_codecs.map do |name, codec|
         
     | 
| 
      
 20 
     | 
    
         
            +
                [
         
     | 
| 
      
 21 
     | 
    
         
            +
                  name, codec, codec.encode(hash.dup)
         
     | 
| 
      
 22 
     | 
    
         
            +
                ]
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              Benchmark.ips do |x|
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                encoded_data.each do |name, codec, encoded|
         
     | 
| 
      
 28 
     | 
    
         
            +
                  x.report(name) do |n|
         
     | 
| 
      
 29 
     | 
    
         
            +
                    while n > 0
         
     | 
| 
      
 30 
     | 
    
         
            +
                      decoded = codec.decode(encoded)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      decoded["user_ids"].include?(user_needle)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      n -= 1
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                x.compare!
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class PackedString
         
     | 
| 
      
 4 
     | 
    
         
            +
              class FastIdList
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.from_array(array, pack_with)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  new(array.sort.pack("#{pack_with}*"), pack_with)
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def self.from_string(string, pack_with)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  new(string, pack_with)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def initialize(packed, pack_with)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  raise "unknown pack format, expecting Q or V" if pack_with != "V" && pack_with != "Q"
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @packed = packed
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @pack_with = pack_with
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @slot_size = pack_with == "V" ? 4 : 8
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def include?(id)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  found = (0...length).bsearch do |index|
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @packed.byteslice(index * @slot_size, @slot_size).unpack1(@pack_with) >= id
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  found && @packed.byteslice(found * @slot_size, @slot_size).unpack1(@pack_with) == id
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def length
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @length ||= @packed.bytesize / @slot_size
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def to_a
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @packed.unpack("#{@pack_with}*")
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @packed
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              def initialize(pack_with = "V")
         
     | 
| 
      
 42 
     | 
    
         
            +
                @pack_with = pack_with
         
     | 
| 
      
 43 
     | 
    
         
            +
                @oj_options = { mode: :compat }
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              def encode(hash)
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                if user_ids = hash["user_ids"]
         
     | 
| 
      
 49 
     | 
    
         
            +
                  hash["user_ids"] = FastIdList.from_array(hash["user_ids"], @pack_with).to_s
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                hash["data"] = ::Oj.dump(hash["data"], @oj_options)
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                Marshal.dump(hash)
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              def decode(payload)
         
     | 
| 
      
 58 
     | 
    
         
            +
                result = Marshal.load(payload)
         
     | 
| 
      
 59 
     | 
    
         
            +
                result["data"] = ::Oj.load(result["data"], @oj_options)
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                if str = result["user_ids"]
         
     | 
| 
      
 62 
     | 
    
         
            +
                  result["user_ids"] = FastIdList.from_string(str, @pack_with)
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                result
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class StringHack
         
     | 
| 
      
 4 
     | 
    
         
            +
              class FastIdList
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.from_array(array)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  new(",#{array.join(",")},")
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def self.from_string(string)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  new(string)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def initialize(packed)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @packed = packed
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def include?(id)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @packed.include?(",#{id},")
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @packed
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 27 
     | 
    
         
            +
                @oj_options = { mode: :compat }
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def encode(hash)
         
     | 
| 
      
 31 
     | 
    
         
            +
                if user_ids = hash["user_ids"]
         
     | 
| 
      
 32 
     | 
    
         
            +
                  hash["user_ids"] = FastIdList.from_array(user_ids).to_s
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                ::Oj.dump(hash, @oj_options)
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              def decode(payload)
         
     | 
| 
      
 39 
     | 
    
         
            +
                result = ::Oj.load(payload, @oj_options)
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                if str = result["user_ids"]
         
     | 
| 
      
 42 
     | 
    
         
            +
                  result["user_ids"] = FastIdList.from_string(str)
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                result
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'bundler/inline'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            gemfile do
         
     | 
| 
      
 6 
     | 
    
         
            +
              source 'https://rubygems.org'
         
     | 
| 
      
 7 
     | 
    
         
            +
              gem 'message_bus', path: '../'
         
     | 
| 
      
 8 
     | 
    
         
            +
              gem 'benchmark-ips'
         
     | 
| 
      
 9 
     | 
    
         
            +
              gem 'oj'
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            require 'benchmark/ips'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require 'message_bus'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require_relative 'codecs/all_codecs'
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            bench_decode({
         
     | 
| 
      
 17 
     | 
    
         
            +
              "data" => "hello world",
         
     | 
| 
      
 18 
     | 
    
         
            +
              "user_ids" => (1..10000).to_a,
         
     | 
| 
      
 19 
     | 
    
         
            +
              "group_ids" => nil,
         
     | 
| 
      
 20 
     | 
    
         
            +
              "client_ids" => nil
         
     | 
| 
      
 21 
     | 
    
         
            +
              }, 5000
         
     | 
| 
      
 22 
     | 
    
         
            +
            )
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            # packed_string_4_bytes:   127176.1 i/s
         
     | 
| 
      
 25 
     | 
    
         
            +
            # packed_string_8_bytes:    94494.6 i/s - 1.35x  (± 0.00) slower
         
     | 
| 
      
 26 
     | 
    
         
            +
            #          string_hack:    26403.4 i/s - 4.82x  (± 0.00) slower
         
     | 
| 
      
 27 
     | 
    
         
            +
            #              marshal:     4985.5 i/s - 25.51x  (± 0.00) slower
         
     | 
| 
      
 28 
     | 
    
         
            +
            #                   oj:     3072.9 i/s - 41.39x  (± 0.00) slower
         
     | 
| 
      
 29 
     | 
    
         
            +
            #                 json:     2222.7 i/s - 57.22x  (± 0.00) slower
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'bundler/inline'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            gemfile do
         
     | 
| 
      
 6 
     | 
    
         
            +
              source 'https://rubygems.org'
         
     | 
| 
      
 7 
     | 
    
         
            +
              gem 'message_bus', path: '../'
         
     | 
| 
      
 8 
     | 
    
         
            +
              gem 'benchmark-ips'
         
     | 
| 
      
 9 
     | 
    
         
            +
              gem 'oj'
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            require 'benchmark/ips'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require 'message_bus'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require_relative 'codecs/all_codecs'
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            bench_decode({
         
     | 
| 
      
 17 
     | 
    
         
            +
              "data" => { amazing: "hello world this is an amazing message hello there!!!", another_key: [2, 3, 4] },
         
     | 
| 
      
 18 
     | 
    
         
            +
              "user_ids" => [1, 2, 3],
         
     | 
| 
      
 19 
     | 
    
         
            +
              "group_ids" => [1],
         
     | 
| 
      
 20 
     | 
    
         
            +
              "client_ids" => nil
         
     | 
| 
      
 21 
     | 
    
         
            +
              }, 2
         
     | 
| 
      
 22 
     | 
    
         
            +
            )
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            #              marshal:   504885.6 i/s
         
     | 
| 
      
 25 
     | 
    
         
            +
            #                 json:   401050.9 i/s - 1.26x  (± 0.00) slower
         
     | 
| 
      
 26 
     | 
    
         
            +
            #                   oj:   340847.4 i/s - 1.48x  (± 0.00) slower
         
     | 
| 
      
 27 
     | 
    
         
            +
            #          string_hack:   296741.6 i/s - 1.70x  (± 0.00) slower
         
     | 
| 
      
 28 
     | 
    
         
            +
            # packed_string_4_bytes:   207942.6 i/s - 2.43x  (± 0.00) slower
         
     | 
| 
      
 29 
     | 
    
         
            +
            # packed_string_8_bytes:   206093.0 i/s - 2.45x  (± 0.00) slower
         
     | 
    
        data/lib/message_bus.rb
    CHANGED
    
    | 
         @@ -2,24 +2,29 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require "monitor"
         
     | 
| 
       4 
4 
     | 
    
         
             
            require "set"
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative "message_bus/version"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative "message_bus/message"
         
     | 
| 
      
 8 
     | 
    
         
            +
            require_relative "message_bus/client"
         
     | 
| 
      
 9 
     | 
    
         
            +
            require_relative "message_bus/connection_manager"
         
     | 
| 
      
 10 
     | 
    
         
            +
            require_relative "message_bus/diagnostics"
         
     | 
| 
      
 11 
     | 
    
         
            +
            require_relative "message_bus/rack/middleware"
         
     | 
| 
      
 12 
     | 
    
         
            +
            require_relative "message_bus/rack/diagnostics"
         
     | 
| 
      
 13 
     | 
    
         
            +
            require_relative "message_bus/timer_thread"
         
     | 
| 
      
 14 
     | 
    
         
            +
            require_relative "message_bus/codec/base"
         
     | 
| 
      
 15 
     | 
    
         
            +
            require_relative "message_bus/backends"
         
     | 
| 
      
 16 
     | 
    
         
            +
            require_relative "message_bus/backends/base"
         
     | 
| 
       13 
17 
     | 
    
         | 
| 
       14 
18 
     | 
    
         
             
            # we still need to take care of the logger
         
     | 
| 
       15 
     | 
    
         
            -
            if defined?(::Rails)
         
     | 
| 
       16 
     | 
    
         
            -
               
     | 
| 
      
 19 
     | 
    
         
            +
            if defined?(::Rails::Engine)
         
     | 
| 
      
 20 
     | 
    
         
            +
              require_relative 'message_bus/rails/railtie'
         
     | 
| 
       17 
21 
     | 
    
         
             
            end
         
     | 
| 
       18 
22 
     | 
    
         | 
| 
       19 
23 
     | 
    
         
             
            # @see MessageBus::Implementation
         
     | 
| 
       20 
24 
     | 
    
         
             
            module MessageBus; end
         
     | 
| 
       21 
25 
     | 
    
         
             
            MessageBus::BACKENDS = {}
         
     | 
| 
       22 
26 
     | 
    
         
             
            class MessageBus::InvalidMessage < StandardError; end
         
     | 
| 
      
 27 
     | 
    
         
            +
            class MessageBus::InvalidMessageTarget < MessageBus::InvalidMessage; end
         
     | 
| 
       23 
28 
     | 
    
         
             
            class MessageBus::BusDestroyed < StandardError; end
         
     | 
| 
       24 
29 
     | 
    
         | 
| 
       25 
30 
     | 
    
         
             
            # The main server-side interface to a message bus for the purposes of
         
     | 
| 
         @@ -277,6 +282,17 @@ module MessageBus::Implementation 
     | 
|
| 
       277 
282 
     | 
    
         
             
                  end
         
     | 
| 
       278 
283 
     | 
    
         
             
              end
         
     | 
| 
       279 
284 
     | 
    
         | 
| 
      
 285 
     | 
    
         
            +
              # @param [MessageBus::Codec::Base] codec used to encode and decode Message payloads
         
     | 
| 
      
 286 
     | 
    
         
            +
              # @return [void]
         
     | 
| 
      
 287 
     | 
    
         
            +
              def transport_codec=(codec)
         
     | 
| 
      
 288 
     | 
    
         
            +
                configure(trasport_codec: codec)
         
     | 
| 
      
 289 
     | 
    
         
            +
              end
         
     | 
| 
      
 290 
     | 
    
         
            +
             
     | 
| 
      
 291 
     | 
    
         
            +
              # @return [MessageBus::Codec::Base] codec used to encode and decode Message payloads
         
     | 
| 
      
 292 
     | 
    
         
            +
              def transport_codec
         
     | 
| 
      
 293 
     | 
    
         
            +
                @config[:transport_codec] ||= MessageBus::Codec::Json.new
         
     | 
| 
      
 294 
     | 
    
         
            +
              end
         
     | 
| 
      
 295 
     | 
    
         
            +
             
     | 
| 
       280 
296 
     | 
    
         
             
              # @param [MessageBus::Backend::Base] pub_sub a configured backend
         
     | 
| 
       281 
297 
     | 
    
         
             
              # @return [void]
         
     | 
| 
       282 
298 
     | 
    
         
             
              def reliable_pub_sub=(pub_sub)
         
     | 
| 
         @@ -329,6 +345,7 @@ module MessageBus::Implementation 
     | 
|
| 
       329 
345 
     | 
    
         
             
              #
         
     | 
| 
       330 
346 
     | 
    
         
             
              # @raise [MessageBus::BusDestroyed] if the bus is destroyed
         
     | 
| 
       331 
347 
     | 
    
         
             
              # @raise [MessageBus::InvalidMessage] if attempting to put permission restrictions on a globally-published message
         
     | 
| 
      
 348 
     | 
    
         
            +
              # @raise [MessageBus::InvalidMessageTarget] if attempting to publish to a empty group of users
         
     | 
| 
       332 
349 
     | 
    
         
             
              def publish(channel, data, opts = nil)
         
     | 
| 
       333 
350 
     | 
    
         
             
                return if @off
         
     | 
| 
       334 
351 
     | 
    
         | 
| 
         @@ -348,22 +365,32 @@ module MessageBus::Implementation 
     | 
|
| 
       348 
365 
     | 
    
         
             
                  site_id = opts[:site_id]
         
     | 
| 
       349 
366 
     | 
    
         
             
                end
         
     | 
| 
       350 
367 
     | 
    
         | 
| 
       351 
     | 
    
         
            -
                 
     | 
| 
      
 368 
     | 
    
         
            +
                if (user_ids || group_ids) && global?(channel)
         
     | 
| 
      
 369 
     | 
    
         
            +
                  raise ::MessageBus::InvalidMessage
         
     | 
| 
      
 370 
     | 
    
         
            +
                end
         
     | 
| 
       352 
371 
     | 
    
         | 
| 
       353 
     | 
    
         
            -
                 
     | 
| 
       354 
     | 
    
         
            -
                   
     | 
| 
       355 
     | 
    
         
            -
             
     | 
| 
       356 
     | 
    
         
            -
             
     | 
| 
       357 
     | 
    
         
            -
             
     | 
| 
       358 
     | 
    
         
            -
             
     | 
| 
      
 372 
     | 
    
         
            +
                if (user_ids == []) || (group_ids == []) || (client_ids == [])
         
     | 
| 
      
 373 
     | 
    
         
            +
                  raise ::MessageBus::InvalidMessageTarget
         
     | 
| 
      
 374 
     | 
    
         
            +
                end
         
     | 
| 
      
 375 
     | 
    
         
            +
             
     | 
| 
      
 376 
     | 
    
         
            +
                encoded_data = transport_codec.encode({
         
     | 
| 
      
 377 
     | 
    
         
            +
                  "data" => data,
         
     | 
| 
      
 378 
     | 
    
         
            +
                  "user_ids" => user_ids,
         
     | 
| 
      
 379 
     | 
    
         
            +
                  "group_ids" => group_ids,
         
     | 
| 
      
 380 
     | 
    
         
            +
                  "client_ids" => client_ids
         
     | 
| 
      
 381 
     | 
    
         
            +
                })
         
     | 
| 
      
 382 
     | 
    
         
            +
             
     | 
| 
      
 383 
     | 
    
         
            +
                channel_opts = {}
         
     | 
| 
       359 
384 
     | 
    
         | 
| 
       360 
     | 
    
         
            -
                 
     | 
| 
      
 385 
     | 
    
         
            +
                if opts
         
     | 
| 
      
 386 
     | 
    
         
            +
                  if ((age = opts[:max_backlog_age]) || (size = opts[:max_backlog_size]))
         
     | 
| 
      
 387 
     | 
    
         
            +
                    channel_opts[:max_backlog_size] = size
         
     | 
| 
      
 388 
     | 
    
         
            +
                    channel_opts[:max_backlog_age] = age
         
     | 
| 
      
 389 
     | 
    
         
            +
                  end
         
     | 
| 
       361 
390 
     | 
    
         | 
| 
       362 
     | 
    
         
            -
             
     | 
| 
       363 
     | 
    
         
            -
             
     | 
| 
       364 
     | 
    
         
            -
             
     | 
| 
       365 
     | 
    
         
            -
                    max_backlog_age: age
         
     | 
| 
       366 
     | 
    
         
            -
                  }
         
     | 
| 
      
 391 
     | 
    
         
            +
                  if opts.has_key?(:queue_in_memory)
         
     | 
| 
      
 392 
     | 
    
         
            +
                    channel_opts[:queue_in_memory] = opts[:queue_in_memory]
         
     | 
| 
      
 393 
     | 
    
         
            +
                  end
         
     | 
| 
       367 
394 
     | 
    
         
             
                end
         
     | 
| 
       368 
395 
     | 
    
         | 
| 
       369 
396 
     | 
    
         
             
                encoded_channel_name = encode_channel_name(channel, site_id)
         
     | 
| 
         @@ -614,7 +641,7 @@ module MessageBus::Implementation 
     | 
|
| 
       614 
641 
     | 
    
         
             
                channel, site_id = decode_channel_name(msg.channel)
         
     | 
| 
       615 
642 
     | 
    
         
             
                msg.channel = channel
         
     | 
| 
       616 
643 
     | 
    
         
             
                msg.site_id = site_id
         
     | 
| 
       617 
     | 
    
         
            -
                parsed =  
     | 
| 
      
 644 
     | 
    
         
            +
                parsed = transport_codec.decode(msg.data)
         
     | 
| 
       618 
645 
     | 
    
         
             
                msg.data = parsed["data"]
         
     | 
| 
       619 
646 
     | 
    
         
             
                msg.user_ids = parsed["user_ids"]
         
     | 
| 
       620 
647 
     | 
    
         
             
                msg.group_ids = parsed["group_ids"]
         
     | 
| 
         @@ -3,8 +3,6 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'redis'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'digest'
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
            require "message_bus/backends/base"
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
6 
     | 
    
         
             
            module MessageBus
         
     | 
| 
       9 
7 
     | 
    
         
             
              module Backends
         
     | 
| 
       10 
8 
     | 
    
         
             
                # The Redis backend stores published messages in Redis sorted sets (using
         
     | 
| 
         @@ -104,8 +102,8 @@ module MessageBus 
     | 
|
| 
       104 
102 
     | 
    
         | 
| 
       105 
103 
     | 
    
         
             
                  local global_id = redis.call("INCR", global_id_key)
         
     | 
| 
       106 
104 
     | 
    
         
             
                  local backlog_id = redis.call("INCR", backlog_id_key)
         
     | 
| 
       107 
     | 
    
         
            -
                  local payload =  
     | 
| 
       108 
     | 
    
         
            -
                  local global_backlog_message =  
     | 
| 
      
 105 
     | 
    
         
            +
                  local payload = table.concat({ global_id, backlog_id, start_payload }, "|")
         
     | 
| 
      
 106 
     | 
    
         
            +
                  local global_backlog_message = table.concat({ backlog_id, channel }, "|")
         
     | 
| 
       109 
107 
     | 
    
         | 
| 
       110 
108 
     | 
    
         
             
                  redis.call("ZADD", backlog_key, backlog_id, payload)
         
     | 
| 
       111 
109 
     | 
    
         
             
                  redis.call("EXPIRE", backlog_key, max_backlog_age)
         
     | 
| 
         @@ -318,7 +316,7 @@ LUA 
     | 
|
| 
       318 
316 
     | 
    
         
             
                        end
         
     | 
| 
       319 
317 
     | 
    
         
             
                      end
         
     | 
| 
       320 
318 
     | 
    
         
             
                    rescue => error
         
     | 
| 
       321 
     | 
    
         
            -
                      @logger.warn "#{error} subscribe failed, reconnecting in 1 second. Call stack #{error.backtrace}"
         
     | 
| 
      
 319 
     | 
    
         
            +
                      @logger.warn "#{error} subscribe failed, reconnecting in 1 second. Call stack #{error.backtrace.join("\n")}"
         
     | 
| 
       322 
320 
     | 
    
         
             
                      sleep 1
         
     | 
| 
       323 
321 
     | 
    
         
             
                      global_redis&.disconnect!
         
     | 
| 
       324 
322 
     | 
    
         
             
                      retry
         
     | 
    
        data/lib/message_bus/client.rb
    CHANGED
    
    | 
         @@ -133,7 +133,6 @@ class MessageBus::Client 
     | 
|
| 
       133 
133 
     | 
    
         
             
                user_allowed = false
         
     | 
| 
       134 
134 
     | 
    
         
             
                group_allowed = false
         
     | 
| 
       135 
135 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
                # this is an inconsistency we should fix anyway, publishing `user_ids: nil` should work same as groups
         
     | 
| 
       137 
136 
     | 
    
         
             
                has_users = msg.user_ids && msg.user_ids.length > 0
         
     | 
| 
       138 
137 
     | 
    
         
             
                has_groups = msg.group_ids && msg.group_ids.length > 0
         
     | 
| 
       139 
138 
     | 
    
         | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MessageBus
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Codec
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Base
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def encode(hash)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    raise ConcreteClassMustImplementError
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def decode(payload)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    raise ConcreteClassMustImplementError
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                autoload :Json, File.expand_path("json", __dir__)
         
     | 
| 
      
 16 
     | 
    
         
            +
                autoload :Oj, File.expand_path("oj", __dir__)
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     |