app_version.rb 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. # Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. class AppVersion
  3. MAINTENANCE_THREAD_SLEEP = 5.seconds
  4. REDIS_RESTART_REQUIRED_KEY = 'zammad_restart_required_timestamp'.freeze
  5. REDIS_RESTART_REQUIRED_TTL = 5.minutes
  6. MSG_APP_VERSION = 'app_version'.freeze
  7. MSG_RESTART_MANUAL = 'restart_manual'.freeze
  8. MSG_RESTART_AUTO = 'restart_auto'.freeze
  9. MSG_CONFIG_CHANGED = 'config_changed'.freeze
  10. class_attribute :_redis
  11. =begin
  12. get current app version
  13. version = AppVersion.get
  14. returns
  15. '20150212131700:0' # 'version:if_browser_reload_is_required'
  16. =end
  17. def self.get
  18. Setting.get('app_version')
  19. end
  20. def self.trigger_browser_reload(type, timestamp: make_timestamp)
  21. return if !Setting.exists?(name: 'app_version')
  22. Setting.set('app_version', timestamp)
  23. Sessions.broadcast(event_data(type), 'public')
  24. Gql::Subscriptions::AppMaintenance.trigger({ type: type })
  25. end
  26. def self.trigger_restart
  27. timestamp = make_timestamp
  28. type = if Setting.get('auto_shutdown')
  29. restart_required!(timestamp)
  30. MSG_RESTART_AUTO
  31. else
  32. MSG_RESTART_MANUAL
  33. end
  34. trigger_browser_reload(type, timestamp:)
  35. end
  36. def self.start_maintenance_thread(process_name:)
  37. return if !Setting.get('auto_shutdown')
  38. initial_app_version = get
  39. Rails.logger.debug { "Starting maintenance thread for #{process_name} (#{Process.pid})" }
  40. Thread.new do
  41. Thread.current.abort_on_exception = true
  42. Thread.current.name = 'app version monitoring'
  43. loop do
  44. if restart_required?(initial_app_version)
  45. Rails.logger.debug { "App version change detected, sending TERM signal to #{process_name} (#{Process.pid})" }
  46. Process.kill('TERM', Process.pid)
  47. break
  48. end
  49. sleep MAINTENANCE_THREAD_SLEEP
  50. end
  51. end
  52. end
  53. def self.event_data(type = 'app_version')
  54. {
  55. event: 'maintenance',
  56. data: {
  57. type: type,
  58. app_version: get,
  59. }
  60. }
  61. end
  62. def self.make_timestamp
  63. Time.current.strftime('%Y%m%d%H%M%S')
  64. end
  65. private_class_method :make_timestamp
  66. def self.restart_required!(timestamp)
  67. redis.set(REDIS_RESTART_REQUIRED_KEY, timestamp, ex: REDIS_RESTART_REQUIRED_TTL)
  68. end
  69. private_class_method :restart_required!
  70. def self.restart_required?(known_app_version)
  71. value = redis.get(REDIS_RESTART_REQUIRED_KEY)
  72. value.present? && (known_app_version != value)
  73. end
  74. private_class_method :restart_required?
  75. def self.redis
  76. self._redis ||= Zammad::Service::Redis.new
  77. end
  78. private_class_method :redis
  79. end