hsdq 0.7.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d34ed2f5f158452f0d837271297827400158f56f
4
+ data.tar.gz: 4129432a2dc752ddfc8d924b6d0d5e98d695099d
5
+ SHA512:
6
+ metadata.gz: 652311c93022e42f60b897c529c002a17c04bf83839e2a1d0cf4b720de7d613fe85312b90c28041ad51a56447ec54cf0a40bb69376c8899120bcc55aca56c3d5
7
+ data.tar.gz: adf4969580bb7075c767b7358c7c6de60d2a75e5682cbbb77177f8ed30ddb01704b00d72fa54e9fd1a4aeaa7292cb9a95a5aa2bd18af475bc7bfd870beebd5e3
@@ -0,0 +1,25 @@
1
+ .rspec
2
+ vendor/bundle
3
+ log/*
4
+ tmp/*
5
+ coverage
6
+ spec/tmp/*
7
+ **.orig
8
+ rerun.txt
9
+ .byebug_history
10
+ .ruby_gemset
11
+ .ruby_version
12
+
13
+ # Ignore bundler config.
14
+ .bundle
15
+ Gemfile.lock
16
+
17
+ # ignore Rubymine files
18
+ .idea/
19
+
20
+ .rvmrc
21
+ .DS_Store
22
+ *.save
23
+ *.swp
24
+ *.mp4
25
+ *.ogv
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.1
@@ -0,0 +1 @@
1
+ --markup markdown
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # see gemspec for the dependencies
4
+ gemspec
@@ -0,0 +1,26 @@
1
+ This file is part of HSDQ
2
+
3
+ Copyright 2013 Yves Lucas,
4
+
5
+ Authorisation is granted under MIT license as long as this copyright and mention are not removed
6
+
7
+ MIT License
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining
10
+ a copy of this software and associated documentation files (the
11
+ "Software"), to deal in the Software without restriction, including
12
+ without limitation the rights to use, copy, modify, merge, publish,
13
+ distribute, sublicense, and/or sell copies of the Software, and to
14
+ permit persons to whom the Software is furnished to do so, subject to
15
+ the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be
18
+ included in all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "debugger"
5
+ require_relative "../lib/hsdq"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,63 @@
1
+ # This file should be in your Rails app config folder, or for script / non Rails app be accessible
2
+ # by your app
3
+ # different host / port can be used to split the different usage
4
+ # Redis is mono threaded so the same server can also run different instances on different ports
5
+ # session can be separate for different group of applications
6
+ # You need one separate file for each hsdq class
7
+ # below setup for 2 instances of Redis:
8
+ # - port 6379 with 2 databases (message db 1 and admin db 0)
9
+ # - port 6380 for session
10
+ # any app/script/class that need to exchange messages together should share the same setup, it is
11
+ # strongly suggested to use same setup for all apps at the beginning.
12
+ # The flexibility of this setup file is for scaling and sharding the busses.
13
+ :development:
14
+ :redis:
15
+ :message:
16
+ :host: 127.0.0.1
17
+ :port: 6379
18
+ :db: 1
19
+ :admin:
20
+ :host: 127.0.0.1
21
+ :port: 6379
22
+ :db: 2
23
+ :session:
24
+ :host: 127.0.0.1
25
+ :port: 6380
26
+ :db: 1
27
+ :exceptions: true
28
+ :threaded: false
29
+ :timeout: 10
30
+ :test:
31
+ :redis:
32
+ :message:
33
+ :host: 127.0.0.1
34
+ :port: 6379
35
+ :db: 2
36
+ :admin:
37
+ :host: 127.0.0.1
38
+ :port: 6379
39
+ :db: 2
40
+ :session:
41
+ :host: 127.0.0.1
42
+ :port: 6379
43
+ :db: 2
44
+ :exceptions: true
45
+ :threaded: false
46
+ :timeout: 10
47
+ :production:
48
+ :redis:
49
+ :message:
50
+ :host: 127.0.0.1
51
+ :port: 6395
52
+ :db: 5
53
+ :admin:
54
+ :host: 127.0.0.1
55
+ :port: 6395
56
+ :db: 5
57
+ :session:
58
+ :host: 127.0.0.1
59
+ :port: 6395
60
+ :db: 5
61
+ :exceptions: false
62
+ :threaded: true
63
+ :timeout: 10
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hsdq/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hsdq"
8
+ spec.version = Hsdq::VERSION
9
+ spec.authors = ["Yves Lucas"]
10
+ spec.email = ["hsdq@ylucas.com"]
11
+
12
+ spec.summary = %q{Hsdq, High Speed Distributed Queue for message bus.}
13
+ spec.description = %q{Hsdq: Light weight and distributed, Hsdq allow message passing distributed applications to exchange requests and data at high speed, work in parallel and scale horizontaly.}
14
+ spec.homepage = "https://github.com/ylucas/hsdq"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|examples)/}) }
18
+ spec.bindir = "bin"
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "redis", "~> 3.3"
22
+ spec.add_runtime_dependency "json", "~> 2.0"
23
+
24
+ spec.add_development_dependency "bundler"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "shoulda-matchers"
28
+ spec.add_development_dependency "yard"
29
+ spec.add_development_dependency "redcarpet"
30
+ spec.add_development_dependency "simplecov"
31
+ spec.add_development_dependency "pry-byebug"
32
+ end
@@ -0,0 +1,38 @@
1
+ # Hsdq: is a High Speed Distributed message Queue built on top of Redis.
2
+ # @see Readme.md
3
+ #
4
+ # This software Copyright 2013-2016 Yves Lucas
5
+ # License MIT, see LICENSE.txt
6
+ #
7
+
8
+ require 'redis'
9
+ require 'json'
10
+ require 'securerandom'
11
+ require 'yaml'
12
+
13
+ require_relative "hsdq/shared"
14
+ require_relative "hsdq/utilities"
15
+ require_relative "hsdq/connectors"
16
+ require_relative "hsdq/listener"
17
+ require_relative "hsdq/sender"
18
+ require_relative "hsdq/setting"
19
+ require_relative "hsdq/receiver"
20
+ require_relative "hsdq/thread_store"
21
+ require_relative "hsdq/session"
22
+ require_relative "hsdq/threadpool"
23
+ require_relative "hsdq/admin"
24
+
25
+ module Hsdq
26
+ include Shared
27
+ include Utilities
28
+ include Connectors
29
+ include Listener
30
+ include Sender
31
+ include Setting
32
+ include Receiver
33
+ include ThreadStore
34
+ include Session
35
+ include Threadpool
36
+ include Admin
37
+
38
+ end
@@ -0,0 +1,60 @@
1
+ module Hsdq
2
+ module Admin
3
+
4
+ def listener_id(listener_id=nil)
5
+ @listener_id = listener_id if listener_id
6
+ @listener_id
7
+ end
8
+
9
+ def listener_version(listener_version=nil)
10
+ @listener_version = listener_version if listener_version
11
+ @listener_version
12
+ end
13
+
14
+ def admin_channel
15
+ "#{environment}_#{channel}_admin"
16
+ end
17
+
18
+ def admin_versionned_channel
19
+ "#{admin_channel}__#{listener_version}"
20
+ end
21
+
22
+ def admin_id_channel
23
+ "#{admin_channel}__#{listener_id}"
24
+ end
25
+
26
+ def admin_channels
27
+ @admin_channels ||= %w(admin )
28
+ end
29
+
30
+ def admin_listener
31
+ begin
32
+ p "starting admin channels #{admin_channel}, #{admin_versionned_channel}"
33
+ cx_admin.subscribe(admin_channel, admin_versionned_channel) do |on|
34
+ on.message do |a_channel, admin_message_j|
35
+ p "received admin message: #{admin_message_j} from admin channel #{admin_channel} "
36
+ process_admin_message(admin_channel, admin_message_j)
37
+ end
38
+ end
39
+ rescue Redis::BaseConnectionError => e
40
+ p e.inspect
41
+ sleep(1)
42
+ retry
43
+ end
44
+ end
45
+
46
+ def process_admin_message(_admin_channel, admin_message_j)
47
+ admin_message = JSON.parse(admin_message_j) rescue {'params' => {'task' => ""}}
48
+ task = admin_message['params'] ? admin_message['params']['task'] : ""
49
+ case task
50
+ when 'stop'
51
+ hsdq_stop!
52
+ when 'start'
53
+ hsdq_start!
54
+ when 'exit'
55
+ hsdq_exit!
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,40 @@
1
+ require 'redis'
2
+
3
+ module Hsdq
4
+
5
+ # This module contains the logic for different connection to the redis layer.
6
+ #
7
+ # They can be connected to the same Redis instance, but it is recommended to use different
8
+ # database connections in order to segregate the different usages.
9
+ #
10
+ # In production of large applications you should use different instances for the different layers.
11
+ # The configuration files are named after your class name hsdq_yourclass.yml
12
+ module Connectors
13
+
14
+ # Establish the listener connection.
15
+ # IMPORTANT this connection is blocked by the listener and must not be used elsewhere
16
+ # @return [Redis connection] For the listener exclusively
17
+ def cx_listener
18
+ @cx_listener ||= Redis.new cx_opts[:message]
19
+ end
20
+
21
+ # Establish an unblocked connection for the sender and also pulling data from Redis
22
+ # @return [Redis connection] This connection is used to send messages as well as to retrieve
23
+ # data from the message hash
24
+ def cx_data
25
+ @cx_data ||= Redis.new cx_opts[:message]
26
+ end
27
+
28
+ # establish an unblocked connection for the session layer
29
+ # @return [Redis connection] reserved for storing and retrieving the sessions data
30
+ def cx_session
31
+ @cx_session ||= Redis.new cx_opts[:session]
32
+ end
33
+
34
+ # establish a connection for the admin channel pub/sub
35
+ # @return [Redis connection] reserved for the admin commands
36
+ def cx_admin
37
+ @cx_admin ||= Redis.new cx_opts[:admin]
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,102 @@
1
+ require "redis"
2
+ require_relative "connectors"
3
+ require_relative "utilities"
4
+
5
+ module Hsdq
6
+ # This module is holding the methods for the class listener.
7
+ # The listener is connected to the Redis instance with the blocking cx_listener connection
8
+ #
9
+ # It is popping the "Spark" (ephemeral part of the message) from the list.
10
+ # When a spark is popped, it is validated and processed in the receiver module.
11
+ module Listener
12
+ include Connectors
13
+
14
+ # Start hsdq to listen to channel.
15
+ # @param [String] channel The channel the hsdq class will be listening
16
+ # @param [Hash] options The hsdq class option from the config file and/or additional parameters passed
17
+ def hsdq_start(channel, options)
18
+ hsdq_add_options(options) if options
19
+ hsdq_start!
20
+ hsdq_loop(channel)
21
+ end
22
+
23
+ # Set the flag to stop processing the queue
24
+ # @return [Boolean] false
25
+ def hsdq_stop!
26
+ @hsdq_running = false
27
+ end
28
+
29
+ # Set the flag to allow processing the queue
30
+ # @return [Boolean] true
31
+ def hsdq_start!
32
+ @hsdq_running = true
33
+ end
34
+
35
+ # Flag allowing or not the processing of the queue
36
+ # @return [Boolean] true when listening is allowed
37
+ def hsdq_running?
38
+ @hsdq_running = true if @hsdq_running.nil?
39
+ @hsdq_running
40
+ end
41
+
42
+ # Opposite or hsdq_running.
43
+ # @see hsdq_running?
44
+ # @return [Boolean] true when listening is not allowed
45
+ def hsdq_stopped?
46
+ !hsdq_running?
47
+ end
48
+
49
+ # When true allow the listener to start
50
+ # When set to false, the listener exit the listening loop. This is mostly to exit gracefully the program
51
+ # The listener needs to be restarted specifically, if need to run again
52
+ # @return [Boolean] true when allowing the listener to stay in listening mode
53
+ def hsdq_alive?
54
+ @hsdq_alive = true if @hsdq_alive.nil?
55
+ @hsdq_alive
56
+ end
57
+
58
+ # Flag break the listening loop if true.
59
+ # @return [Boolean]
60
+ def hsdq_exit?
61
+ @hsdq_exit
62
+ end
63
+
64
+ # Set exit to force the listening loop to exit.
65
+ def hsdq_exit!
66
+ @hsdq_exit = true
67
+ end
68
+
69
+ # stops the listening loop
70
+ def kill_alive!
71
+ @hsdq_alive = false
72
+ end
73
+
74
+ # Start the listener
75
+ # :nocov:
76
+ def start_listener(options={})
77
+ Thread.new { hsdq_start(channel, options) }
78
+ Thread.new { admin_listener }
79
+ end
80
+ # :nocov:
81
+
82
+ private
83
+ # Listening loop. Listen to the channel using a blocking left pop.
84
+ # The timeout allow the idle process to watch at regular interval if there is an admin command.
85
+ # The process is listeing only if there is available thread to be started in the pool
86
+ def hsdq_loop(channel)
87
+ p "starting listening channel #{channel}"
88
+ while hsdq_alive?
89
+ if (allow_new_threads? || !hsdq_opts[:threaded]) && hsdq_running?
90
+ raw_spark = cx_listener.blpop(channel, hsdq_opts[:timeout] )
91
+ hsdq_ignit raw_spark, hsdq_opts if raw_spark
92
+ else
93
+ # :nocov:
94
+ sleep 0.01 # occur only when hsdq_max_threads is reached
95
+ # :nocov:
96
+ end
97
+ kill_alive! if hsdq_exit?
98
+ end
99
+ end
100
+
101
+ end
102
+ end