1
\$\begingroup\$

I need comments on the below code:

Thread.new {EM.run do
              IpamAgent::Amqp.run
            end}



module IpamAgent
  class Amqp
    class << self
      def run
        begin
          $connection = AMQP.connect(RMQ_CONFIGURATIONS)
          $connection.on_tcp_connection_loss do |conn, settings|
            Rails.logger.info  "<<<<<<<<<<<<<<<< [network failure] Trying to reconnect...>>>>>>>>>>>>>>>>>>>>>>>>"
            conn.reconnect(false, 2)
          end
          Rails.logger.info "<<<<<<<<<<<<<<<<<<<<<<<<AMQP listening>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
          worker   = IpamAgent::MessageHandler.new
          worker.start
          Rails.logger.info "<<<<<<<<<<<<<<<<<<<<<<<<Message handler started>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"

        rescue Exception => e
          Rails.logger.info "<<<<<<<<<<<<<<<<<<<<<<<<Message handler Exception>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
          Rails.logger.info "[error] Could not handle event of type #{e.inspect}"
          Rails.logger.info "<<<<<<<<<<<<<<<<<<<<<<<<Message handler Exception>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
        end
      end
    end
  end
end




    module IpamAgent

      class MessageHandler
        attr_accessor :ns_exchange, :sc_exchange, :ns_queue, :sc_queue, :service_location, :service_version, :service_name, :message_sequence_id, :presto_exchange, :presto_queue

        def initialize
          # Profile the code
          @ns_exchange = CONFIGURATIONS["ns_exchange"]
          @sc_exchange = CONFIGURATIONS["sc_exchange"]
          @presto_exchange =  CONFIGURATIONS["presto_exchange"]

          @ns_queue = CONFIGURATIONS["ns_queue"]
          @sc_queue = CONFIGURATIONS["sc_queue"]
          @presto_queue =  CONFIGURATIONS["presto_queue"]
        end

        # Create the channels, exchanges and queues
        def start
          ch1  = AMQP::Channel.new($connection)
          ch2  = AMQP::Channel.new($connection)
          ch3  = AMQP::Channel.new($connection)

          @ns_x = ch1.direct(ns_exchange, :durable => true)
          @ns_queue   = ch1.queue(ns_queue, :auto_delete => false)
          @ns_queue.bind(@ns_x, :routing_key => @ns_queue.name).subscribe(:ack => true, &method(:handle_ns_message))

          @sc_x = ch2.topic(sc_exchange, :durable => true)
          @sc_queue   = ch2.queue(sc_queue, :auto_delete => false)
          @sc_queue.bind(@sc_x, :routing_key => "#").subscribe(:ack => true, &method(:handle_sc_message))

          @presto_x = ch3.direct(presto_exchange, :durable => true)
          @presto_queue   = ch3.queue(presto_queue, :auto_delete => false)
          @presto_queue.bind(@presto_x, :routing_key => @presto_queue.name).subscribe(:ack => true, &method(:handle_presto_message))
        end

        # Handle the messages from Network service component
        def handle_ns_message(headers, payload)
          message_headers = JSON.parse(headers.to_json)["headers"]
          payload = eval(payload)
          headers.ack
          Rails.logger.info ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>MESSAGE FROM NS<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
          Rails.logger.info message_headers
          Rails.logger.info payload
          Rails.logger.info ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>MESSAGE FROM NS<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"

          tenant_detail = IpamAgent::TenantDetail.where(service_instance: payload["orgId"]).first
          if(payload && payload.keys.include?(:responseCode))
            new_tenant_detail = IpamAgent::TenantDetail.create(message: ({"header" => message_headers, "payload" => payload}), status: "waiting_for_sc", service_instance: payload["orgId"])
            if tenant_detail && tenant_detail.service_group
              publish_sgid_to_presto(tenant_detail)
            else
              get_sgid_from_sc(new_tenant_detail)
            end
          else
            Rails.logger.info("Payload: #{payload}, routing key is #{message_headers}")
          end
        end

        # Retrieve the Service Group ID from Service controller
        def get_sgid_from_sc(tenant_detail)
          message = tenant_detail.get_sc_message
          Rails.logger.info(">>>>>>>>>>>>>>>>>>>>PUBLISHING TO SC<<<<<<<<<<<<<<<<<<<<<<<")
          Rails.logger.info(message)
          Rails.logger.info(">>>>>>>>>>>>>>>>>>>>PUBLISHING TO SC<<<<<<<<<<<<<<<<<<<<<<<")
          @sc_x.publish(message.last,  :routing_key => @sc_queue.name, :headers => message.first, :mandatory => true)
        end

        # Handle the messages from Service controller
        def handle_sc_message(headers, payload)
          message_headers = JSON.parse(headers.to_json)
          headers.ack
          payload = eval(payload)
          Rails.logger.info(">>>>>>>>>>>>>>>>>>>>MESSAGE FROM SC<<<<<<<<<<<<<<<<<<<<<<<")
          Rails.logger.info(message_headers)
          Rails.logger.info(payload)
          Rails.logger.info(">>>>>>>>>>>>>>>>>>>>MESSAGE FROM SC<<<<<<<<<<<<<<<<<<<<<<<")
          if(payload && payload["serviceInstanceGroupId"])
            tenant_detail = IpamAgent::TenantDetail.find_or_save(payload)
            publish_sgid_to_presto(tenant_detail)
          end
        end

        # Shovel the NS request to PMP with Service Group ID
        def publish_sgid_to_presto(tenant_detail)
          tenant_details = TenantDetail.where(service_instance: tenant_detail.service_instance, status: "waiting_for_sc")
          tenant_details.each do |sc|
            sc.update(status: "success")
            message = sc.get_pmp_message
            Rails.logger.info(">>>>>>>>>>>>>>>>>>>>PUBLISHING TO PRESTO<<<<<<<<<<<<<<<<<<<<<<<")
            Rails.logger.info(message.first)
            Rails.logger.info(message.last)
            Rails.logger.info(">>>>>>>>>>>>>>>>>>>>PUBLISHING TO PRESTO<<<<<<<<<<<<<<<<<<<<<<<")
            @presto_x.publish(message.last,  :routing_key => @presto_queue.name, :headers => message.first, :mandatory => true)
          end
        end

        # Receive the message from PMP presto and publsih it to Network Service
        def handle_presto_message(headers, payload)
          message_headers = JSON.parse(headers.to_json)
          payload = eval(payload)
          Rails.logger.info(">>>>>>>>>>>>>>>>>>>>MESSAGE FROM PRESTO<<<<<<<<<<<<<<<<<<<<<<<")
          Rails.logger.info(headers.to_json)
          Rails.logger.info(payload)
          Rails.logger.info(">>>>>>>>>>>>>>>>>>>>MESSAGE FROM PRESTO<<<<<<<<<<<<<<<<<<<<<<<")
          headers.ack
          @ns_x.publish(payload,  :routing_key =>@ns_queue.name, :headers => headers, :mandatory => true) if (message_headers && payload)
        end
      end

    end
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Don't use global variables
Using $connection as a holder for your AMQP connection is a bad idea. connection being such a generic name, it might be used somewhere else for something completely different, and you may end up breaking your code with someone else's.

A better idea would be to use class variables - this way you get your namespace for free:

module IpamAgent
  class Amqp
    class << self
      attr_reader connection

      def run
        begin
          @connection = AMQP.connect(RMQ_CONFIGURATIONS)
          connection.on_tcp_connection_loss do |conn, settings|
            # ...
end

And now your usage will be:

def start
  ch1 = AMQP::Channel.new(IpamAgent::Amqp.connection)
  # ...
end

Don't change the meaning of a variable
When you set a variable with one type, for one usage, but then set the same variable with another type, for a different usage - you are confusing yourself, and future code-readers, and make your code very brittle. This goes double for instance variables, and triple for instance variables exposed to the outside - what do you think a user will expect from this code:

  worker = IpamAgent::MessageHandler.new
  queue = worker.ns_queue
  worker.start
  same_queue = worker.ns_queue

Avoid noisy code, and noisy logs
Your code is filled with < and > characters, which are meant to highlight sections in your log files, but since it is used so much, your log file will be filled with marquees, and will be painful to read (just like you code right now, only much worse).

You should differentiate between events in the log using log levels - exceptions and errors should be logged using Rails.logger.error, production-level events should be logged using Rails.logger.info. All other events and debug data should be logged only using Rails.logger.debug, and maybe not at all.

Variable naming
Using names such as ch, ns, sc, etc. is very discouraged, unless it is part of your domain's nomenclature. For example, ns for a lot of people coming from XML, means namespace. Be verbal - use full names (network_service).

If it needs a comment - you probably have to rename it or refactor it
You have many methods with comments explaining what they do. Comments are generally a bad idea, since they tend to "rot" - when you change the code, most often than not, you neglect to maintain the comments (For example what does # Profile the code refer to?).

There are two types of method comments in your code - those which repeat the name of the method, and don't add much (handle_network_service_message is enough, you don't need to say # Handle the messages from Network service component...), and those which explain things which are not explained by the method name.

Delete the ones which repeat the name of the method.

The other kind hints that you probably need to either rename the method, or break it down to its parts. You could either rename start to create_channels, or (probably better) have a create_channel method which start will call 3 times - it will make your code DRYier.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.