Skip to content

Counter API#1857

Merged
repeatedly merged 23 commits into
masterfrom
counter-api
Apr 21, 2018
Merged

Counter API#1857
repeatedly merged 23 commits into
masterfrom
counter-api

Conversation

@repeatedly

Copy link
Copy Markdown
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
Copy Markdown
Member Author

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

@okkez

okkez commented Feb 21, 2018

Copy link
Copy Markdown
Contributor

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.

@okkez okkez left a comment

Copy link
Copy Markdown
Contributor

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.

Comment thread lib/fluent/system_config.rb Outdated
config_param :scope, :string

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

Copy link
Copy Markdown
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

Comment thread lib/fluent/system_config.rb Outdated

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

Copy link
Copy Markdown
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

Comment thread lib/fluent/counter/client.rb Outdated
@port = opt[:port] || DEFAULT_PORT
@addr = opt[:addr] || DEFAULT_ADDR
@log = opt[:log] || $log
@conn = Connection.connect(@addr, @port, method(:on_message))

Copy link
Copy Markdown
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

Comment thread lib/fluent/supervisor.rb Outdated

def after_run
stop_rpc_server if @rpc_endpoint
stop_counter_server if @counter_endpoint

Copy link
Copy Markdown
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.

Comment thread lib/fluent/counter/client.rb Outdated
exist_scope!
params = [params] unless params.is_a?(Array)
res = send_request('inc', @scope, params, options)
options[:async] ? res : res.get

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Member Author

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

@okkez

okkez commented Apr 17, 2018

Copy link
Copy Markdown
Contributor

@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

repeatedly commented Apr 17, 2018

Copy link
Copy Markdown
Member Author

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

okkez commented Apr 17, 2018

Copy link
Copy Markdown
Contributor

Looks good to me 👍

Signed-off-by: Masahiro Nakagawa <repeatedly@gmail.com>
@repeatedly

Copy link
Copy Markdown
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
Copy Markdown
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