Browse Source

Invited spring to the party ๐ŸŽ‰ Taking development speed to the next level ๐Ÿš€- Enable via ENV 'ENABLE_SPRING'.

Thorsten Eckel 6 years ago
parent
commit
6718d2eb7c
10 changed files with 128 additions and 44 deletions
  1. 5 0
      Gemfile
  2. 9 0
      Gemfile.lock
  3. 5 0
      bin/rails
  4. 5 0
      bin/rake
  5. 5 0
      bin/rspec
  6. 6 4
      bin/spring
  7. 37 0
      config/spring.rb
  8. 15 0
      config/spring_client.rb
  9. 20 27
      script/scheduler.rb
  10. 21 13
      script/websocket-server.rb

+ 5 - 0
Gemfile

@@ -120,6 +120,11 @@ gem 'viewpoint'
 # in production environments by default.
 group :development, :test do
 
+  # app boottime improvement
+  gem 'spring'
+  gem 'spring-commands-rspec'
+  gem 'spring-commands-testunit'
+
   # debugging
   gem 'byebug'
   gem 'pry-rails'

+ 9 - 0
Gemfile.lock

@@ -415,6 +415,12 @@ GEM
       simplecov (>= 0.4.1)
     slack-notifier (2.3.1)
     slop (3.6.0)
+    spring (2.0.2)
+      activesupport (>= 4.2)
+    spring-commands-rspec (1.0.4)
+      spring (>= 0.9.1)
+    spring-commands-testunit (1.0.1)
+      spring (>= 0.9.1)
     sprockets (3.7.2)
       concurrent-ruby (~> 1.0)
       rack (> 1, < 3)
@@ -558,6 +564,9 @@ DEPENDENCIES
   simplecov
   simplecov-rcov
   slack-notifier
+  spring
+  spring-commands-rspec
+  spring-commands-testunit
   sprockets
   sqlite3
   tcr

+ 5 - 0
bin/rails

@@ -1,4 +1,9 @@
 #!/usr/bin/env ruby
+begin
+  load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+  raise unless e.message.include?('spring')
+end
 APP_PATH = File.expand_path('../../config/application', __FILE__)
 require_relative '../config/boot'
 require 'rails/commands'

+ 5 - 0
bin/rake

@@ -1,4 +1,9 @@
 #!/usr/bin/env ruby
+begin
+  load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+  raise unless e.message.include?('spring')
+end
 require_relative '../config/boot'
 require 'rake'
 Rake.application.run

+ 5 - 0
bin/rspec

@@ -1,3 +1,8 @@
 #!/usr/bin/env ruby
+begin
+  load File.expand_path('spring', __dir__)
+rescue LoadError => e
+  raise unless e.message.include?('spring')
+end
 require 'bundler/setup'
 load Gem.bin_path('rspec-core', 'rspec')

+ 6 - 4
bin/spring

@@ -3,13 +3,15 @@
 # This file loads spring without using Bundler, in order to be fast.
 # It gets overwritten when you run the `spring binstub` command.
 
-unless defined?(Spring)
+if ENV['ENABLE_SPRING'] && !defined?(Spring)
   require 'rubygems'
   require 'bundler'
 
-  if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^    (?:  )*spring \((.*?)\)$.*?^$/m))
-    Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) }
-    gem 'spring', match[1]
+  lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
+  spring = lockfile.specs.detect { |spec| spec.name == "spring" }
+  if spring
+    Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
+    gem 'spring', spring.version
     require 'spring/binstub'
   end
 end

+ 37 - 0
config/spring.rb

@@ -0,0 +1,37 @@
+module Spring
+  module Commands
+    class SchedulerRb
+
+      def call
+        load ::Rails.root.join('script/scheduler.rb')
+      end
+    end
+
+    Spring.register_command 'scheduler.rb', Spring::Commands::SchedulerRb.new
+  end
+end
+
+module Spring
+  module Commands
+    class WebsocketServerRb
+
+      def call
+        load ::Rails.root.join('script/websocket-server.rb')
+      end
+    end
+
+    Spring.register_command 'websocket-server.rb', Spring::Commands::WebsocketServerRb.new
+  end
+end
+
+module Spring
+  module Commands
+    class RailsServer < Rails
+      def command_name
+        'server'
+      end
+    end
+
+    Spring.register_command 'rails_server', RailsServer.new
+  end
+end

+ 15 - 0
config/spring_client.rb

@@ -0,0 +1,15 @@
+module Spring
+  module Client
+    class Rails < Command
+
+      DEFAULT_COMMANDS = COMMANDS.dup
+      DEFAULT_ALIASES  = ALIASES.dup
+
+      remove_const('COMMANDS')
+      remove_const('ALIASES')
+
+      const_set('COMMANDS', DEFAULT_COMMANDS + %w[server])
+      const_set('ALIASES', DEFAULT_ALIASES.merge('s' => 'server'))
+    end
+  end
+end

+ 20 - 27
script/scheduler.rb

@@ -1,27 +1,18 @@
 #!/usr/bin/env ruby
 # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
+begin
+  load File.expand_path('../bin/spring', __dir__)
+rescue LoadError => e
+  raise unless e.message.include?('spring')
+end
 
-$LOAD_PATH << './lib'
-require 'rubygems'
-
-# load rails env
 dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
 Dir.chdir dir
-RAILS_ENV = ENV['RAILS_ENV'] || 'development'
 
-require 'rails/all'
 require 'bundler'
-require File.join(dir, 'config', 'environment')
 require 'daemons'
 
 def before_fork
-
-  # clear all connections before for, reconnect later ActiveRecord::Base.connection.reconnect!
-  # issue #1405 - Scheduler not running because of Bad file descriptor in PGConsumeInput()
-  # https://github.com/zammad/zammad/issues/1405
-  # see also https://bitbucket.org/ged/ruby-pg/issues/260/frequent-crashes-with-multithreading
-  ActiveRecord::Base.clear_all_connections!
-
   # remember open file handles
   @files_to_reopen = []
   ObjectSpace.each_object(File) do |file|
@@ -38,6 +29,15 @@ def after_fork(dir)
     file.sync = true
   end
 
+  # Spring redirects STDOUT and STDERR to /dev/null
+  # before we get here. This causes the `reopen` lines
+  # below to fail because the handles are already
+  # opened for write
+  if defined?(Spring)
+    $stdout.close
+    $stderr.close
+  end
+
   $stdout.reopen("#{dir}/log/scheduler_out.log", 'w')
   $stderr.reopen("#{dir}/log/scheduler_err.log", 'w')
 end
@@ -45,29 +45,22 @@ end
 before_fork
 
 daemon_options = {
-  multiple: false,
-  dir_mode: :normal,
-  dir: File.join(dir, 'tmp', 'pids'),
+  multiple:  false,
+  dir_mode:  :normal,
+  dir:       File.join(dir, 'tmp', 'pids'),
   backtrace: true
 }
 
-name = 'scheduler'
-Daemons.run_proc(name, daemon_options) do
-
-  if ARGV.include?('--')
-    ARGV.slice! 0..ARGV.index('--')
-  else
-    ARGV.clear
-  end
+Daemons.run_proc('scheduler', daemon_options) do
 
   after_fork(dir)
 
-  Rails.logger.info 'Scheduler started.'
+  require File.join(dir, 'config', 'environment')
 
+  Rails.logger.info 'Scheduler started.'
   at_exit do
     Rails.logger.info 'Scheduler stopped.'
   end
 
-  require 'scheduler'
   Scheduler.threads
 end

+ 21 - 13
script/websocket-server.rb

@@ -1,27 +1,26 @@
 #!/usr/bin/env ruby
 # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
+begin
+  load File.expand_path('../bin/spring', __dir__)
+rescue LoadError => e
+  raise unless e.message.include?('spring')
+end
 
-$LOAD_PATH << './lib'
-require 'rubygems'
-
-# load rails env
-dir = File.expand_path('..', __dir__)
+dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
 Dir.chdir dir
-RAILS_ENV = ENV['RAILS_ENV'] || 'development'
 
-require 'rails/all'
 require 'bundler'
+
 require File.join(dir, 'config', 'environment')
+
 require 'eventmachine'
 require 'em-websocket'
 require 'json'
 require 'fileutils'
 require 'optparse'
 require 'daemons'
-require 'sessions'
 
 def before_fork
-
   # remember open file handles
   @files_to_reopen = []
   ObjectSpace.each_object(File) do |file|
@@ -38,8 +37,17 @@ def after_fork(dir)
     file.sync = true
   end
 
-  $stdout.reopen( "#{dir}/log/websocket-server_out.log", 'w').sync = true
-  $stderr.reopen( "#{dir}/log/websocket-server_err.log", 'w').sync = true
+  # Spring redirects STDOUT and STDERR to /dev/null
+  # before we get here. This causes the `reopen` lines
+  # below to fail because the handles are already
+  # opened for write
+  if defined?(Spring)
+    $stdout.close
+    $stderr.close
+  end
+
+  $stdout.reopen("#{dir}/log/websocket-server_out.log", 'w').sync = true
+  $stderr.reopen("#{dir}/log/websocket-server_err.log", 'w').sync = true
 end
 
 before_fork
@@ -53,7 +61,7 @@ before_fork
   d: false,
   k: '/path/to/server.key',
   c: '/path/to/server.crt',
-  i: Dir.pwd.to_s + '/tmp/pids/websocket.pid'
+  i: "#{dir}/tmp/pids/websocket.pid"
 }
 
 tls_options = {}
@@ -111,7 +119,7 @@ if ARGV[0] == 'start' && @options[:d]
   Daemons.daemonize(
     app_name: File.basename(@options[:i], '.pid'),
     dir_mode: :normal,
-    dir: File.dirname(@options[:i])
+    dir:      File.dirname(@options[:i])
   )
 
   after_fork(dir)