Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Counter API #1857

Merged
merged 23 commits into from
Apr 21, 2018
Merged

Counter API #1857

merged 23 commits into from
Apr 21, 2018

Conversation

repeatedly
Copy link
Member

Update version of #1241
We continue to update a patch here.

@repeatedly repeatedly added the v1 label Feb 14, 2018
@repeatedly repeatedly self-assigned this Feb 14, 2018
@repeatedly
Copy link
Member Author

@okkez If you want to test counter-api, use this branch instead.

@okkez
Copy link
Contributor

okkez commented Feb 21, 2018

I've tried to test counter-api but it seems that counter-api is wok in progress.
Fluentd does not call any counter-api (client). So I cannot test counter-api with Fluentd and fluent-plugin-xxx, for now.

I will try to add some ad-hoc patch and test counter-api with Fluentd.

Copy link
Contributor

@okkez okkez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed some points.

config_param :scope, :string

desc 'endpoint of counter server'
config_param :endpoint, :string # host:port
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about splitting host and port for consistency with other plugins such as in_forward and in_monitor_agent?
It is useful to set the default value for those parameters.

config_section :counter_server, multi: false do
  config_param :bind, :string
  config_param :port, :integer
end


config_section :counter_client, multi: false do
desc 'endpoint of counter client'
config_param :endpoint, :string # host:port
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about following? It is useful to set the default value for those parameters.

config_sectio :counter_client, multi: false do
  config_param :host, :string
  config_param :port, :integer
end

@port = opt[:port] || DEFAULT_PORT
@addr = opt[:addr] || DEFAULT_ADDR
@log = opt[:log] || $log
@conn = Connection.connect(@addr, @port, method(:on_message))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about following?
Fluent::Counter::Client is used by plugin developers and Counter API users. Is this right?
opt ={} does not describe details of parameters. Thus users have to read the code to understand this API.
We may also need plugin helper to create Counter API client for plugins.

def initialize(event_loop: Coolio::Loop.new, log: $log, host: '127.0.0.1', port: 4321)
  @event_loop = event_loop
  @host = host
  @port = port
  @log = log
  @conn = Connection.connect(@host, @port, method(:on_message))
  # ...
end

socket_manager_path = ServerEngine::SocketManager::Server.generate_path
ServerEngine::SocketManager::Server.open(socket_manager_path)
ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = socket_manager_path.to_s
end

def after_run
stop_rpc_server if @rpc_endpoint
stop_counter_server if @counter_endpoint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@counter_endpoint is removed in 056a344.

exist_scope!
params = [params] unless params.is_a?(Array)
res = send_request('inc', @scope, params, options)
options[:async] ? res : res.get
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about like following?

      def inc(params, options: {})
        exist_scope!
        params = [params] unless params.is_a?(Array)
        res = send_request('inc', @scope, params, options)
        if options[:async]
          if block_given?
            Thread.start do
              yield res.get
            end
          else
            res
          end
        else
          if block_given?
            yield res.get
          else
            res.get
          end
        end
      end

We can use this as following:

@counter_client = ...
@counter_client.inc({ name: "counter", value: 1}, options: { async: true }) do |response|
   # use response here
end

In the original code, we must handle future instance always when we send request asynchronously.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added suggested API.

def get
# Block until `set` method is called and @result is set a value
join if @result.nil?
@result
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about changing the return value to Response object from plain Hash?

class Response
   attr_reader :errors, :data
   def initialize(result)
      @errors = result["errors"]
      @data = result["data"]
      # ...
   end

  def success?
    @errors.nil? || @errors.empty?
  end

  def error?
    !success?
  end
end

It is useful to check Counter API response as following:

@counter_client.inc({ name: "counter", value: 1}, options: { async: true }) do |response|
   if response.success?
     log.debug("Update counter successfully")
   else
     log.warn("Counter API error: #{response.errors}")
   end
end

Original code:

future = @counter_client.inc({ name: "counter", value: 1}, options: { async: true })
Thread.start do
  response = future.get
  if future.errors?
     log.error("failure")
  else
     log.debug("success")
  end
end

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I will apply the changes soon.

@repeatedly
Copy link
Member Author

@okkez Do you have other suggestion? If not, I have a plan to merge this PR.

@okkez
Copy link
Contributor

okkez commented Apr 17, 2018

@repeatedly I want a shorthand for @counter_client.inc({ name: "name", value: 1 }, options: { async: true }). It is annoying to specify options: { async: true } for each asynchronous request.

How about that the counter client sends an asynchronous request by default?

Or, how about that plugin helper creates an asynchronous client?

async_client = counter_client_create(scope: "datacounter", async: true)
async_client.inc(name: "name", value: 1) # send an asynchronous request
async_client.inc(name: "name", value: 1, options: { async: false }) # send a synchronous request

Or else...?
Anyway, I want a shorthand to send asynchronous requests.

@repeatedly
Copy link
Member Author

repeatedly commented Apr 17, 2018

How about that the counter client sends an asynchronous request by default?

I see. One idea is removing sync API. Adding .get is low cost and it is not messy in user code.

future = client.inc(name: "name", value: 1)  # async
result = client.inc(name: "name", value: 1).get  # get result in sync

How about this?

@okkez
Copy link
Contributor

okkez commented Apr 17, 2018

Looks good to me 👍

Signed-off-by: Masahiro Nakagawa <repeatedly@gmail.com>
@repeatedly
Copy link
Member Author

Remove sync API

@repeatedly repeatedly mentioned this pull request Apr 17, 2018
@repeatedly repeatedly merged commit ee4a192 into master Apr 21, 2018
@repeatedly
Copy link
Member Author

Merged. We will improve Counter API with actual plugins.

@ganmacs ganmacs deleted the counter-api branch July 11, 2019 08:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants