norikra 0.1.4-java → 0.1.5-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d5ba6d34ed7bba46947b8434b0ac32b8cb7ba2d
4
- data.tar.gz: 2c6a5434cfc175aab9de2f89d3ab10198a75a06f
3
+ metadata.gz: 30a26c93805e62b832b47ade931907e800c35c01
4
+ data.tar.gz: f8fe030eacb414c84aeebd01b40351965391a53b
5
5
  SHA512:
6
- metadata.gz: 48a696a0f5611dc7657c82e1e119a10355da055eadfd4417b0456508a345ce4a18bc8617bd4aa46435cf49c85c7e0863ef42e01747923de89a0695e9ea7a86db
7
- data.tar.gz: 9b04d5197dd10a38c6d66dd09ae580509f2469c1d6727f774e203e61400796accc5c3de164df2262b0d0521959c49bb80bd8241648e339f234d75881b8994a71
6
+ metadata.gz: 243a44cf8003f1843df7137c4c78c9e72765e3da51f7f33e961531cd9017c612389d44051fdf2a1cf98c57149b97a9978c250dacc98a4cd324206e9866d4cdbd
7
+ data.tar.gz: 7351f6ee5139a5323adfb9708abe8291f253e47a783d2efcc4fdf682c35f706ea48ae7c179dcfa8bb6bea73137eb95bdd2b578c164ec4af4de24e237cb2efe9b
data/README.md CHANGED
@@ -69,6 +69,29 @@ See: http://norikra.github.io/
69
69
 
70
70
  ## Changes
71
71
 
72
+ * v0.1.5:
73
+ * Add RPC port monitoring handler (GET /)
74
+ * Add `Pattern` support
75
+ * Changes for GC/JVM
76
+ * Server starts with default(recommended) JVM options for GC configurations
77
+ * Add option not to use default JVM options
78
+ * Add option to configure JVM to print GC logs
79
+ * Stats file changes
80
+ * Changed to use SIGUSR2 (SIGUSR1 is used by JVM on linux platform)
81
+ * Changed no to contain server process configurations (ports, threads, logs ...)
82
+ * WebUI improvements
83
+ * Add button to close target on WebUI
84
+ * Add link to download stats file
85
+ * Fix bugs
86
+ * Fieldname handlings about names with non-alphabetic chars
87
+ * Query parser bug for some built-in functions (ex: `rate(10)`)
88
+ * Write stats file in more safe way
89
+ * WebUI: Incorrect memory bar
90
+ * v0.1.4:
91
+ * Stat dump option on runtime per specified intervals (`--dump-stat-interval`)
92
+ * Stat dump on demand by SIGUSR1
93
+ * `norikra-client event see` command (and API call) to get query results, but not remove it
94
+ * Last modified time for each target on WebUI
72
95
  * v0.1.3:
73
96
  * Fix critical bug about query de-registration
74
97
  * v0.1.2:
data/lib/norikra/cli.rb CHANGED
@@ -13,11 +13,12 @@ module Norikra
13
13
  ### Server options
14
14
  option :host, :type => :string, :default => nil, :aliases => "-H", :desc => 'host address that server listen [0.0.0.0]'
15
15
  option :port, :type => :numeric, :default => nil, :aliases => "-P", :desc => 'port that server uses [26571]'
16
+ option :'ui-port', :type => :numeric, :default => nil, :desc => 'port that Web UI server uses [26578]'
16
17
 
17
18
  option :stats, :type => :string, :default => nil, :aliases => "-s", \
18
- :desc => 'status file path to load/dump targets, queries and server configurations [none]'
19
+ :desc => 'status file path to load/dump targets and queries [none]'
19
20
  option :'suppress-dump-stat', :type => :boolean, :default => false, \
20
- :desc => 'specify not to update stat file with updated targets/queries/configurations on runtime [false]'
21
+ :desc => 'specify not to update stat file with updated targets/queries on runtime [false]'
21
22
  option :'dump-stat-interval', :type => :numeric, :default => nil, \
22
23
  :desc => 'interval(seconds) of status file dumps on runtime [none (on shutdown only)]'
23
24
 
@@ -29,6 +30,10 @@ module Norikra
29
30
  option :outfile, :type => :string, :default => nil, \
30
31
  :desc => "stdout redirect file when daemonized [${logdir}/norikra.out]"
31
32
 
33
+ ### JVM options
34
+ option :'bare-jvm', :type => :boolean, :default => false, :desc => "use JVM without any recommended options"
35
+ option :'gc-log', :type => :string, :default => nil, :desc => "output gc logs on specified file path"
36
+
32
37
  ### Performance options
33
38
  # performance predefined configuration sets
34
39
  option :micro, :type => :boolean, :default => false, \
@@ -71,6 +76,40 @@ module Norikra
71
76
  end
72
77
 
73
78
  class CLI < Thor
79
+ # JVM defaults w/ ConcurrentGC:
80
+ # java -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -version
81
+ # (jruby does not modify flags)
82
+ #
83
+ # CMSIncrementalMode: not recommended in Java8
84
+ #
85
+ # CMSInitiatingOccupancyFraction: CMS threshold
86
+ # CMSInitiatingOccupancyFraction = 100 - MinHeapFreeRatio + CMSTriggerRatio * MinHeapFreeRatio / 100
87
+ # default: MinHeapFreeRatio=40 CMSTriggerRatio=80 -> CMSInitiatingOccupancyFraction=92
88
+ #
89
+ # NewRatio=7 (New:Old = 1:7)
90
+ # InitialSurvivorRatio=8 MinSurvivorRatio=3 SurvivorRatio=8 (Eden:Survivor0:survivor1 = 8:1:1)
91
+ #
92
+ # MaxTenuringThreshold=4
93
+ # TargetSurvivorRatio=50
94
+ # (InitialTenuringThreshold=7 is for Parallel GC/UseAdaptiveSizePolicy)
95
+ #
96
+ # SoftRefLRUPolicyMSPerMB=1000
97
+ # ( gc_interval > free_heap * ms_per_mb : clear softref )
98
+
99
+ JVM_OPTIONS = [
100
+ '-XX:-UseGCOverheadLimit',
101
+ '-XX:+UseConcMarkSweepGC', '-XX:+UseCompressedOops',
102
+ '-XX:CMSInitiatingOccupancyFraction=70', '-XX:+UseCMSInitiatingOccupancyOnly',
103
+ '-XX:NewRatio=1',
104
+ '-XX:SurvivorRatio=2', '-XX:MaxTenuringThreshold=15', '-XX:TargetSurvivorRatio=80',
105
+ '-XX:SoftRefLRUPolicyMSPerMB=200',
106
+ ]
107
+
108
+ JVM_GC_OPTIONS = [
109
+ '-verbose:gc', '-XX:+PrintGCDetails', '-XX:+PrintGCDateStamps',
110
+ '-XX:+HeapDumpOnOutOfMemoryError',
111
+ ]
112
+
74
113
  ### 'start' and 'serverprocess' have almost same option set (for parse/help)
75
114
  ### DIFF: jvm options (-X)
76
115
  Norikra::CLIUtil.register_common_start_options(self)
@@ -85,11 +124,22 @@ module Norikra
85
124
  ARGV.shift # shift head "start"
86
125
 
87
126
  argv = ["serverproc"]
88
- jruby_options = ['-J-server', '-J-XX:-UseGCOverheadLimit']
127
+ jruby_options = ['-J-server']
128
+
129
+ unless options[:'bare-jvm']
130
+ jruby_options += JVM_OPTIONS.map{|opt| '-J' + opt }
131
+ end
132
+
133
+ if options[:'gc-log']
134
+ jruby_options += JVM_GC_OPTIONS.map{|opt| '-J' + opt }
135
+ jruby_options.push "-J-Xloggc:#{options[:'gc-log']}"
136
+ end
89
137
 
90
138
  ARGV.each do |arg|
91
139
  if arg =~ /^-X(.+)$/
92
140
  jruby_options.push('-J-X' + $1)
141
+ elsif arg =~ /^-verbose:gc$/
142
+ jruby_options.push('-J-verbose:gc')
93
143
  else
94
144
  argv.push(arg)
95
145
  end
@@ -171,7 +221,13 @@ module Norikra
171
221
  level: loglevel, dir: options[:logdir], filesize: options[:'log-filesize'], backups: options[:'log-backups'],
172
222
  }
173
223
 
174
- server = Norikra::Server.new( options[:host], options[:port], conf )
224
+ server_options = {
225
+ host: options[:host],
226
+ port: options[:port],
227
+ ui_port: options[:'ui-port'],
228
+ }
229
+
230
+ server = Norikra::Server.new( server_options, conf )
175
231
  server.run
176
232
  server.shutdown
177
233
  end
data/lib/norikra/field.rb CHANGED
@@ -218,7 +218,23 @@ module Norikra
218
218
  raise ArgumentError, "container_accessor must be a String or Interger, but #{accessor.class.to_s}"
219
219
  end
220
220
  if v.is_a?(Hash)
221
- v[accessor] || v[accessor.to_s] # hash[string] is valid, and hash[int] is also valid, and hash["int"] is valid too.
221
+ # v[accessor] || v[accessor.to_s]
222
+ if v.has_key?(accessor)
223
+ v[accessor]
224
+ elsif v.has_key?(accessor.to_s)
225
+ v[accessor.to_s]
226
+ elsif accessor.is_a?(String)
227
+ val = v.keys.reduce(nil) do |r, key|
228
+ if key.is_a?(String)
229
+ r || ( key.gsub(/[^$_a-zA-Z0-9]/, '_') == accessor.to_s ? v[key] : nil )
230
+ else
231
+ r
232
+ end
233
+ end
234
+ val # valid value or nil
235
+ else # v does not have key "#{accessor}" and accessor is Fixnum
236
+ nil
237
+ end
222
238
  elsif v.is_a?(Array)
223
239
  if accessor.is_a?(Fixnum)
224
240
  v[accessor]
data/lib/norikra/query.rb CHANGED
@@ -58,13 +58,13 @@ module Norikra
58
58
 
59
59
  def targets
60
60
  return @targets if @targets
61
- @targets = (self.ast.listup(:stream).map(&:target) + self.subqueries.map(&:targets).flatten).sort.uniq
61
+ @targets = (self.ast.listup(:stream).map(&:targets).flatten + self.subqueries.map(&:targets).flatten).sort.uniq
62
62
  @targets
63
63
  end
64
64
 
65
65
  def aliases
66
66
  return @aliases if @aliases
67
- @aliases = (self.ast.listup(:stream).map(&:alias) + self.subqueries.map(&:aliases).flatten).sort.uniq
67
+ @aliases = (self.ast.listup(:stream).map{|s| s.aliases.map(&:first) }.flatten + self.subqueries.map(&:aliases).flatten).sort.uniq
68
68
  @aliases
69
69
  end
70
70
 
@@ -81,10 +81,12 @@ module Norikra
81
81
  all = []
82
82
  unknowns = []
83
83
  self.ast.listup(:stream).each do |node|
84
- if node.alias
85
- alias_map[node.alias] = node.target
84
+ node.aliases.each do |alias_name, target|
85
+ alias_map[alias_name] = target
86
+ end
87
+ node.targets.each do |target|
88
+ fields[target] ||= []
86
89
  end
87
- fields[node.target] = []
88
90
  end
89
91
 
90
92
  dup_aliases = (alias_map.keys & fields.keys)
@@ -79,34 +79,84 @@ module Norikra
79
79
  # ["LIB_FUNC_CHAIN", ["LIB_FUNCTION", "opts.num.seq", "length", "("]],
80
80
  # "0"]]]]
81
81
 
82
+ ### SELECT a.name, a.content, b.content
83
+ ### FROM pattern[every a=EventA -> b=EventA(name = a.name, type = 'TYPE') WHERE timer:within(1 min)].win:time(2 hour)
84
+ ### WHERE a.source in ('A', 'B')
85
+
86
+ # ["EPL_EXPR",
87
+ # ["SELECTION_EXPR",
88
+ # ["SELECTION_ELEMENT_EXPR",
89
+ # ["EVENT_PROP_EXPR",
90
+ # ["EVENT_PROP_SIMPLE", "a"],
91
+ # ["EVENT_PROP_SIMPLE", "name"]]],
92
+ # ["SELECTION_ELEMENT_EXPR",
93
+ # ["EVENT_PROP_EXPR",
94
+ # ["EVENT_PROP_SIMPLE", "a"],
95
+ # ["EVENT_PROP_SIMPLE", "content"]]],
96
+ # ["SELECTION_ELEMENT_EXPR",
97
+ # ["EVENT_PROP_EXPR",
98
+ # ["EVENT_PROP_SIMPLE", "b"],
99
+ # ["EVENT_PROP_SIMPLE", "content"]]]],
100
+ # ["STREAM_EXPR",
101
+ # ["PATTERN_INCL_EXPR",
102
+ # ["FOLLOWED_BY_EXPR",
103
+ # ["FOLLOWED_BY_ITEM", ["every", ["PATTERN_FILTER_EXPR", "a", "EventA"]]],
104
+ # ["FOLLOWED_BY_ITEM",
105
+ # ["GUARD_EXPR",
106
+ # ["PATTERN_FILTER_EXPR",
107
+ # "b",
108
+ # "EventA",
109
+ # ["EVAL_EQUALS_EXPR",
110
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "name"]],
111
+ # ["EVENT_PROP_EXPR",
112
+ # ["EVENT_PROP_SIMPLE", "a"],
113
+ # ["EVENT_PROP_SIMPLE", "name"]]],
114
+ # ["EVAL_EQUALS_EXPR",
115
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "type"]],
116
+ # "'TYPE'"]],
117
+ # "timer",
118
+ # "within",
119
+ # ["TIME_PERIOD", ["MINUTE_PART", "1"]]]]]],
120
+ # ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["HOUR_PART", "2"]]]],
121
+ # ["WHERE_EXPR",
122
+ # ["in",
123
+ # ["EVENT_PROP_EXPR",
124
+ # ["EVENT_PROP_SIMPLE", "a"],
125
+ # ["EVENT_PROP_SIMPLE", "source"]],
126
+ # "(",
127
+ # "'A'",
128
+ # "'B'",
129
+ # ")"]]]
130
+
82
131
  def astnode(tree)
83
132
  children = if tree.children
84
133
  tree.children.map{|c| astnode(c)}
85
134
  else
86
135
  []
87
136
  end
88
- case tree.text
89
- when 'EVENT_PROP_EXPR'
90
- ASTEventPropNode.new(tree.text, children)
91
- when 'SELECTION_ELEMENT_EXPR'
92
- ASTSelectionElementNode.new(tree.text, children)
93
- when 'LIB_FUNCTION'
94
- ASTLibFunctionNode.new(tree.text, children)
95
- when 'STREAM_EXPR'
96
- ASTStreamNode.new(tree.text, children)
97
- when 'SUBSELECT_EXPR'
98
- ASTSubSelectNode.new(tree.text, children)
137
+ cls = case tree.text
138
+ when 'EVENT_PROP_EXPR' then ASTEventPropNode
139
+ when 'SELECTION_ELEMENT_EXPR' then ASTSelectionElementNode
140
+ when 'LIB_FUNCTION' then ASTLibFunctionNode
141
+ when 'STREAM_EXPR' then ASTStreamNode
142
+ when 'PATTERN_FILTER_EXPR' then ASTPatternNode
143
+ when 'SUBSELECT_EXPR' then ASTSubSelectNode
144
+ else ASTNode
145
+ end
146
+ if cls.respond_to?(:generate)
147
+ cls.generate(tree.text, children, tree)
99
148
  else
100
- ASTNode.new(tree.text, children)
149
+ cls.new(tree.text, children, tree)
101
150
  end
102
151
  end
103
152
 
104
153
  class ASTNode
105
- attr_accessor :name, :children
154
+ attr_accessor :name, :children, :tree
106
155
 
107
- def initialize(name, children)
156
+ def initialize(name, children, tree)
108
157
  @name = name
109
158
  @children = children
159
+ @tree = tree
110
160
  end
111
161
 
112
162
  def nodetype?(*sym)
@@ -132,7 +182,12 @@ module Norikra
132
182
  nil
133
183
  end
134
184
 
135
- def listup(type) # search all nodes that has 'type'
185
+ def listup(*type) # search all nodes that has 'type'
186
+ if type.size > 1
187
+ return type.map{|t| self.listup(t) }.reduce(&:+)
188
+ end
189
+ type = type.first
190
+
136
191
  result = []
137
192
  result.push(self) if type.is_a?(String) && @name == type || nodetype?(type)
138
193
 
@@ -184,15 +239,22 @@ module Norikra
184
239
  end
185
240
 
186
241
  class ASTLibFunctionNode < ASTNode # LIB_FUNCTION
187
- # "now()" => ["LIB_FUNCTION", "now", "("]
188
- # "hoge.length()" => ["LIB_FUNCTION", "hoge", "length", "("]
189
- # "hoge.substr(0)" => ["LIB_FUNCTION", "hoge", "substr", "0", "("]
190
- # "substr(10,0)" => ["LIB_FUNCTION", "substr", "10", "0", "("]
191
- # "hoge.substr(0,8)" => ["LIB_FUNCTION", "hoge", "substr", "0", "8", "("]
242
+ ### foo is function
243
+ # "foo()" => ["LIB_FUNCTION", "foo", "("]
244
+ # "foo(10)" => ["LIB_FUNCTION", "foo", "10", "("]
245
+ # "foo(10,0)" => ["LIB_FUNCTION", "foo", "10", "0", "("]
246
+ # "foo(bar)" => ["LIB_FUNCTION", "foo", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bar"]], "("]
247
+
248
+ ### foo is property
249
+ # "foo.bar()" => ["LIB_FUNCTION", "foo", "bar", "("]
250
+ # "foo.bar(0)" => ["LIB_FUNCTION", "foo", "bar", "0", "("]
251
+ # "foo.bar(0,8)" => ["LIB_FUNCTION", "foo", "bar", "0", "8", "("]
192
252
 
193
- # "opts.num.$0.length()" => ["LIB_FUNCTION", "opts.num.$0", "length", "("]
253
+ ### nested field access
254
+ # "foo.bar.$0.baz()" => ["LIB_FUNCTION", "foo.bar.$0", "baz", "("]
194
255
 
195
- # "max(size)" => ["LIB_FUNCTION", "max", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "("]
256
+ # 2nd child is bare-word (like [a-z][a-z0-9]*) -> this is function -> 1st child is receiver -> property
257
+ # 2nd child is literal or property or none -> 1st child is built-in function
196
258
 
197
259
  def nodetype?(*sym)
198
260
  sym.include?(:lib) || sym.include?(:libfunc)
@@ -207,7 +269,7 @@ module Norikra
207
269
  # first element should be func name if second element is property, library call or subqueries
208
270
  self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
209
271
 
210
- elsif @children[1].name =~ /^(-)?\d+(\.\d+)$/ || @children[1].name =~ /^'[^']*'$/ || @children[1].name =~ /^"[^"]*"$/
272
+ elsif @children[1].name =~ /^(-)?\d+(\.\d+)?$/ || @children[1].name =~ /^'[^']*'$/ || @children[1].name =~ /^"[^"]*"$/
211
273
  # first element should be func name if secod element is number/string literal
212
274
  self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
213
275
 
@@ -216,7 +278,7 @@ module Norikra
216
278
  self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
217
279
 
218
280
  else
219
- # first element may be property
281
+ # first element may be property ########## or function
220
282
  # * simple 'fieldname.funcname()'
221
283
  # * fully qualified 'target.fieldname.funcname()'
222
284
  # * simple/fully-qualified container field access 'fieldname.key.$0.funcname()' or 'target.fieldname.$1.funcname()'
@@ -237,31 +299,146 @@ module Norikra
237
299
  end
238
300
 
239
301
  class ASTStreamNode < ASTNode # STREAM_EXPR
302
+ def self.generate(name, children, tree)
303
+ if children.first.name == 'EVENT_FILTER_EXPR'
304
+ ASTStreamEventNode.new(name, children, tree)
305
+ elsif children.first.name == 'PATTERN_INCL_EXPR'
306
+ ASTStreamPatternNode.new(name, children, tree)
307
+ else
308
+ raise "unexpected stream node type! report to norikra developer!: #{children.map(&:name).join(',')}"
309
+ end
310
+ end
311
+
312
+ def nodetype?(*sym)
313
+ sym.include?(:stream)
314
+ end
315
+
316
+ def targets
317
+ # ["TARGET_NAME"]
318
+ raise NotImplementedError, "ASTStreamNode#targets MUST be overridden by subclass"
319
+ end
320
+
321
+ def aliases
322
+ # [ [ "ALIAS_NAME", "TARGET_NAME" ], ... ]
323
+ raise NotImplementedError, "ASTStreamNode#aliases MUST be overridden by subclass"
324
+ end
325
+
326
+ def fields(default_target=nil, known_targets_aliases=[])
327
+ raise NotImplementedError, "ASTStreamNode#fields MUST be overridden by subclass"
328
+ end
329
+ end
330
+
331
+ class ASTStreamEventNode < ASTStreamNode
332
+ ##### from stream_def [as name] [unidirectional] [retain-union | retain-intersection],
333
+ ##### [ stream_def ... ]
334
+ #
335
+ # single Event stream name ( ex: FROM events.win:time(...) AS e )
336
+ #
240
337
  # ["STREAM_EXPR",
241
338
  # ["EVENT_FILTER_EXPR", "FraudWarningEvent"],
242
339
  # ["VIEW_EXPR", "win", "keepall"],
243
340
  # "fraud"],
341
+ #
244
342
  # ["STREAM_EXPR",
245
343
  # ["EVENT_FILTER_EXPR",
246
344
  # "PINChangeEvent",
247
345
  # [">", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "10"]],
248
346
  # ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["SECOND_PART", "20"]]]],
249
347
 
348
+ NON_ALIAS_NODES = ['EVENT_FILTER_EXPR','VIEW_EXPR','unidirectional','retain-union','retain-intersection']
349
+
350
+ def targets
351
+ [self.find('EVENT_FILTER_EXPR').child.name]
352
+ end
353
+
354
+ def aliases
355
+ alias_nodes = children.select{|n| not NON_ALIAS_NODES.include?(n.name) }
356
+ if alias_nodes.size < 1
357
+ []
358
+ elsif alias_nodes.size > 1
359
+ raise "unexpected FROM clause (includes 2 or more alias words): #{alias_nodes.map(&:name).join(',')}"
360
+ else
361
+ [ [ alias_nodes.first.name, self.targets.first ] ]
362
+ end
363
+ end
364
+
365
+ def fields(default_target=nil, known_targets_aliases=[])
366
+ this_target = self.targets.first
367
+ self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target,known_targets_aliases)}.reduce(&:+) || []
368
+ end
369
+ end
370
+
371
+ class ASTStreamPatternNode < ASTStreamNode
372
+ ## MEMO: Pattern itself can have alias name, but it makes no sense. So we ignore it.
373
+ ## ('x' is ignored): pattern [... ] AS x
374
+ #
375
+ # pattern ( ex: FROM pattern[ every a=events1 -> b=Events1(name=a.name, type='T') where timer:within(1 min) ].win:time(2 hour) )
376
+ #
377
+ # ["STREAM_EXPR",
378
+ # ["PATTERN_INCL_EXPR",
379
+ # ["FOLLOWED_BY_EXPR",
380
+ # ["FOLLOWED_BY_ITEM", ["every", ["PATTERN_FILTER_EXPR", "a", "EventA"]]],
381
+ # ["FOLLOWED_BY_ITEM",
382
+ # ["GUARD_EXPR",
383
+ # ["PATTERN_FILTER_EXPR",
384
+ # "b",
385
+ # "EventA",
386
+ # ["EVAL_EQUALS_EXPR",
387
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "name"]],
388
+ # ["EVENT_PROP_EXPR",
389
+ # ["EVENT_PROP_SIMPLE", "a"],
390
+ # ["EVENT_PROP_SIMPLE", "name"]]],
391
+ # ["EVAL_EQUALS_EXPR",
392
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "type"]],
393
+ # "'TYPE'"]],
394
+ # "timer",
395
+ # "within",
396
+ # ["TIME_PERIOD", ["MINUTE_PART", "1"]]]]]],
397
+ # ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["HOUR_PART", "2"]]]],
398
+
399
+ def targets
400
+ self.listup(:pattern).map(&:target)
401
+ end
402
+
403
+ def aliases
404
+ self.listup(:pattern).map{|p| [ p.alias, p.target ] }
405
+ end
406
+
407
+ def fields(default_target=nil, known_targets_aliases=[])
408
+ self.listup(:pattern).map{|p| p.fields(default_target, known_targets_aliases) }.reduce(&:+) || []
409
+ end
410
+ end
411
+
412
+ class ASTPatternNode < ASTNode
413
+ # ["PATTERN_FILTER_EXPR", "a", "EventA"]
414
+ #
415
+ # ["PATTERN_FILTER_EXPR",
416
+ # "b",
417
+ # "EventA",
418
+ # ["EVAL_EQUALS_EXPR",
419
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "name"]],
420
+ # ["EVENT_PROP_EXPR",
421
+ # ["EVENT_PROP_SIMPLE", "a"],
422
+ # ["EVENT_PROP_SIMPLE", "name"]]],
423
+ # ["EVAL_EQUALS_EXPR",
424
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "type"]],
425
+ # "'TYPE'"]],
426
+
250
427
  def nodetype?(*sym)
251
- sym.include?(:stream)
428
+ sym.include?(:pattern)
252
429
  end
253
430
 
254
431
  def target
255
- self.find('EVENT_FILTER_EXPR').child.name
432
+ children[1].name
256
433
  end
257
434
 
258
435
  def alias
259
- @children.last.children.size < 1 ? @children.last.name : nil
436
+ children[0].name
260
437
  end
261
438
 
262
439
  def fields(default_target=nil, known_targets_aliases=[])
263
440
  this_target = self.target
264
- self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target,known_targets_aliases)}.reduce(&:+) || []
441
+ self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target, known_targets_aliases) }.reduce(&:+) || []
265
442
  end
266
443
  end
267
444
 
@@ -0,0 +1,18 @@
1
+ require 'norikra/logger'
2
+ include Norikra::Log
3
+
4
+ require 'norikra/rpc'
5
+
6
+ class Norikra::RPC::Gatekeeper
7
+ # handler of "GET /" to respond for monitor tools
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ if env["REQUEST_METHOD"] == 'GET' && env["PATH_INFO"] == '/'
14
+ return [ 200, {'Content-Type' => 'text/plain'}, ['Norikra alive!'] ]
15
+ end
16
+ return @app.call(env)
17
+ end
18
+ end
@@ -3,6 +3,7 @@ require 'rack/builder'
3
3
  require 'msgpack-rpc-over-http-jruby'
4
4
 
5
5
  require_relative 'handler'
6
+ require_relative 'gatekeeper'
6
7
 
7
8
  require 'norikra/logger'
8
9
  include Norikra::Log
@@ -25,6 +26,7 @@ module Norikra::RPC
25
26
  @threads = opts[:threads] || DEFAULT_THREADS
26
27
  handler = Norikra::RPC::Handler.new(@engine)
27
28
  @app = Rack::Builder.new {
29
+ use Norikra::RPC::Gatekeeper
28
30
  run MessagePack::RPCOverHTTP::Server.app(handler)
29
31
  }
30
32
  end
@@ -43,13 +43,13 @@ module Norikra
43
43
  :web => { threads: 2 },
44
44
  }
45
45
 
46
- def self.threading_configuration(conf, stats)
46
+ def self.threading_configuration(conf)
47
47
  threads = case conf[:predefined]
48
48
  when :micro then MICRO_PREDEFINED
49
49
  when :small then SMALL_PREDEFINED
50
50
  when :middle then MIDDLE_PREDEFINED
51
51
  when :large then LARGE_PREDEFINED
52
- else (stats ? stats.threads : MICRO_PREDEFINED)
52
+ else MICRO_PREDEFINED
53
53
  end
54
54
  [:inbound, :outbound, :route_exec, :timer_exec].each do |type|
55
55
  [:threads, :capacity].each do |item|
@@ -61,15 +61,15 @@ module Norikra
61
61
  threads
62
62
  end
63
63
 
64
- def self.log_configuration(conf, stats)
65
- logconf = stats ? stats.log : { level: nil, dir: nil, filesize: nil, backups: nil }
64
+ def self.log_configuration(conf)
65
+ logconf = { level: nil, dir: nil, filesize: nil, backups: nil }
66
66
  [:level, :dir, :filesize, :backups].each do |sym|
67
67
  logconf[sym] = conf[sym] if conf[sym]
68
68
  end
69
69
  logconf
70
70
  end
71
71
 
72
- def initialize(host, port, conf={})
72
+ def initialize(server_options, conf={})
73
73
  if conf[:daemonize]
74
74
  outfile_path = conf[:daemonize][:outfile] || File.join(conf[:log][:dir], 'norikra.out')
75
75
  Dir.chdir("/")
@@ -89,12 +89,12 @@ module Norikra
89
89
  nil
90
90
  end
91
91
 
92
- @host = host || (@stats ? @stats.host : nil)
93
- @port = port || (@stats ? @stats.port : nil)
94
- @ui_port = @stats ? @stats.ui_port : nil
92
+ @host = server_options[:host] || Norikra::RPC::HTTP::DEFAULT_LISTEN_HOST
93
+ @port = server_options[:port] || Norikra::RPC::HTTP::DEFAULT_LISTEN_PORT
94
+ @ui_port = server_options[:ui_port] || Norikra::WebUI::HTTP::DEFAULT_LISTEN_PORT
95
95
 
96
- @thread_conf = self.class.threading_configuration(conf[:thread], @stats)
97
- @log_conf = self.class.log_configuration(conf[:log], @stats)
96
+ @thread_conf = self.class.threading_configuration(conf[:thread])
97
+ @log_conf = self.class.log_configuration(conf[:log])
98
98
 
99
99
  Norikra::Log.init(@log_conf[:level], @log_conf[:dir], {:filesize => @log_conf[:filesize], :backups => @log_conf[:backups]})
100
100
 
@@ -144,7 +144,7 @@ module Norikra
144
144
  info "Norikra server started."
145
145
 
146
146
  shutdown_proc = ->{ @running = false }
147
- # JVM uses SIGQUIT for thread/heap state dumping
147
+ # JVM uses SIGQUIT / SIGUSR1 for thread/heap state dumping
148
148
  [:INT, :TERM].each do |s|
149
149
  Signal.trap(s, shutdown_proc)
150
150
  end
@@ -155,14 +155,14 @@ module Norikra
155
155
  else
156
156
  nil
157
157
  end
158
- Signal.trap(:USR1, ->{ @dump_stats = true })
158
+ Signal.trap(:USR2, ->{ @dump_stats = true })
159
159
 
160
- #TODO: SIGHUP?(dynamic plugin loading?) SIGUSR2?
160
+ #TODO: SIGHUP?(dynamic plugin loading?)
161
161
 
162
162
  while @running
163
163
  sleep 0.3
164
164
 
165
- if @stats && !@stats_suppress_dump
165
+ if @stats_path && !@stats_suppress_dump
166
166
  if @dump_stats || (@dump_next_time && Time.now > @dump_next_time)
167
167
  dump_stats
168
168
  @dump_stats = false
@@ -203,21 +203,7 @@ module Norikra
203
203
  end
204
204
 
205
205
  def dump_stats
206
- stats = Norikra::Stats.new(
207
- host: @host,
208
- port: @port,
209
- threads: @thread_conf,
210
- log: @log_conf,
211
- targets: @engine.targets.map{|t|
212
- {
213
- :name => t.name,
214
- :fields => @engine.typedef_manager.dump_target(t.name),
215
- :auto_field => t.auto_field
216
- }
217
- },
218
- queries: @engine.queries.map(&:dump)
219
- )
220
- stats.dump(@stats_path)
206
+ Norikra::Stats.generate(@engine).dump(@stats_path)
221
207
  info "Current status saved", :path => @stats_path
222
208
  end
223
209
  end
data/lib/norikra/stats.rb CHANGED
@@ -2,27 +2,40 @@ require 'json'
2
2
 
3
3
  module Norikra
4
4
  class Stats
5
- attr_accessor :host, :port, :ui_port, :threads, :log
6
5
  attr_accessor :targets, :queries
7
6
 
7
+ def self.generate(engine)
8
+ Norikra::Stats.new(
9
+ targets: engine.targets.map{|t|
10
+ {
11
+ :name => t.name,
12
+ :fields => engine.typedef_manager.dump_target(t.name),
13
+ :auto_field => t.auto_field
14
+ }
15
+ },
16
+ queries: engine.queries.map(&:dump)
17
+ )
18
+ end
19
+
8
20
  def initialize(opts={})
9
- @host = opts[:host]
10
- @port = opts[:port]
11
- @ui_port = opts[:ui_port]
12
- @threads = opts[:threads]
13
- @log = opts[:log]
14
21
  @targets = opts[:targets] || []
15
22
  @queries = opts[:queries] || []
16
23
  end
17
24
 
18
25
  def to_hash
19
- {host: @host, port: @port, ui_port: @ui_port, threads: @threads, log: @log, targets: @targets, queries: @queries}
26
+ {targets: @targets, queries: @queries}
27
+ end
28
+
29
+ def to_json
30
+ JSON.pretty_generate(self.to_hash)
20
31
  end
21
32
 
22
33
  def dump(path)
23
- File.open(path, 'w') do |file|
24
- file.write(JSON.pretty_generate(self.to_hash))
34
+ tmp_path = path + '.tmp'
35
+ File.open(tmp_path, 'w') do |file|
36
+ file.write(self.to_json)
25
37
  end
38
+ File.rename(tmp_path, path)
26
39
  end
27
40
 
28
41
  def self.load(path)
@@ -1,3 +1,3 @@
1
1
  module Norikra
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -4,6 +4,8 @@ require 'norikra/logger'
4
4
 
5
5
  require 'norikra/webui'
6
6
 
7
+ require 'norikra/stats'
8
+
7
9
  require 'sinatra/base'
8
10
  require 'sinatra/json'
9
11
  require 'erubis'
@@ -87,6 +89,14 @@ class Norikra::WebUI::Handler < Sinatra::Base
87
89
  end
88
90
  end
89
91
 
92
+ post '/close' do
93
+ target_name = params[:target]
94
+ logging(:manage, :close, [target_name]) do
95
+ engine.close(target_name)
96
+ redirect '/'
97
+ end
98
+ end
99
+
90
100
  post '/register' do
91
101
  query_name,query_group,expression = params[:query_name], params[:query_group], params[:expression]
92
102
 
@@ -120,6 +130,15 @@ class Norikra::WebUI::Handler < Sinatra::Base
120
130
  end
121
131
  end
122
132
 
133
+ get '/stat/dump' do
134
+ logging(:show, :stat_dump) do
135
+ stats = Norikra::Stats.generate(engine)
136
+ date = Time.now.strftime('%Y-%m-%d')
137
+ attachment "stats.dump.#{date}.json"
138
+ stats.to_json
139
+ end
140
+ end
141
+
123
142
  get '/json/query/:name' do
124
143
  query_name = params[:name]
125
144
  logging(:show, :json_query, [query_name]) do
data/spec/field_spec.rb CHANGED
@@ -275,5 +275,18 @@ describe Norikra::Field do
275
275
  f.define_value_accessor('foo.bar.1.baz', true)
276
276
  expect(f.value({'foo' => {'bar' => [{'baz' => 'value1'}, {'baz' => 'value2'}]}})).to eql('value2')
277
277
  end
278
+
279
+ it 'defines chain value accessor with field name including escaped field' do
280
+ f = Norikra::Field.new('foo', 'hash')
281
+ f.define_value_accessor('foo.bar_baz.bar_baz', true)
282
+ data = {
283
+ 'foo' => {
284
+ 'bar baz' => {
285
+ 'bar/baz' => 'ichi',
286
+ }
287
+ }
288
+ }
289
+ expect(f.value(data)).to eql('ichi')
290
+ end
278
291
  end
279
292
  end
data/spec/query_spec.rb CHANGED
@@ -24,6 +24,23 @@ describe Norikra::Query do
24
24
  end
25
25
  end
26
26
 
27
+ context 'with top-level built-in functions' do
28
+ it 'returns query instances collectly parsed' do
29
+ expression = 'SELECT rate(10) FROM TestTable output snapshot every 2 sec'
30
+ q = Norikra::Query.new(
31
+ :name => 'TestTable query1', :expression => expression
32
+ )
33
+ expect(q.name).to eql('TestTable query1')
34
+ expect(q.group).to be_nil
35
+ expect(q.expression).to eql(expression)
36
+ expect(q.targets).to eql(['TestTable'])
37
+
38
+ expect(q.fields).to eql([])
39
+ expect(q.fields('TestTable')).to eql([])
40
+ expect(q.fields(nil)).to eql([])
41
+ end
42
+ end
43
+
27
44
  context 'with order by' do
28
45
  it 'returns query instances, collectly parsed, without AS_names for fields' do
29
46
  expression = 'SELECT name.string, count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND size > 100 and param.length() > 0 GROUP BY name.string ORDER BY cnt'
@@ -159,6 +176,24 @@ describe Norikra::Query do
159
176
  expect(q.fields(nil)).to eql([])
160
177
  end
161
178
  end
179
+
180
+ context 'with query with patterns' do
181
+ it 'returns query instances collectly parsed' do
182
+
183
+ expression = "select a.name, a.content, b.content from pattern [every a=EventA -> b=EventA(name = a.name, type = 'TYPE') where timer:within(1 min)].win:time(2 hour) where a.source in ('A', 'B')"
184
+ q = Norikra::Query.new(
185
+ :name => 'TestTable query9', :expression => expression
186
+ )
187
+ expect(q.name).to eql('TestTable query9')
188
+ expect(q.group).to be_nil
189
+ expect(q.expression).to eql(expression)
190
+ expect(q.targets).to eql(['EventA'])
191
+ expect(q.aliases).to eql(['a', 'b'])
192
+ expect(q.fields).to eql(['name', 'content', 'type', 'source'].sort)
193
+ expect(q.fields('EventA')).to eql(['name', 'content', 'type', 'source'].sort)
194
+ expect(q.fields(nil)).to eql([])
195
+ end
196
+ end
162
197
  end
163
198
 
164
199
  describe '#dup' do
data/spec/stats_spec.rb CHANGED
@@ -9,11 +9,6 @@ describe Norikra::Stats do
9
9
  describe '#to_hash' do
10
10
  it 'returns internal stats as hash with symbolized keys' do
11
11
  args = {
12
- host: nil,
13
- port: nil,
14
- ui_port: nil,
15
- threads: Norikra::Server::SMALL_PREDEFINED,
16
- log: {level: nil, dir: nil, filesize: nil, backups: nil},
17
12
  targets: [],
18
13
  queries: [],
19
14
  }
@@ -27,11 +22,6 @@ describe Norikra::Stats do
27
22
  Dir.mktmpdir do |dir|
28
23
  File.open("#{dir}/stats.json", 'w') do |file|
29
24
  args = {
30
- host: nil,
31
- port: nil,
32
- ui_port: nil,
33
- threads: Norikra::Server::LARGE_PREDEFINED,
34
- log: {level: 'WARN', dir: '/var/log/norikra', filesize: '50MB', backups: 300},
35
25
  targets: [
36
26
  { name: 'test1', fields: { id: { name: 'id', type: 'int', optional: false}, data: { name: 'data', type: 'string', optional: true } } },
37
27
  ],
data/views/index.erb CHANGED
@@ -14,7 +14,7 @@
14
14
  <div class="progress-bar progress-bar-<%= usage_bar_color %>" style="width: <%= used_p.to_i %>%;">
15
15
  <span class="sr-only"><%= used_p %> % used</span>
16
16
  </div>
17
- <div class="progress-bar progress-bar-success" style="width: <%= committed_p.to_i %>%;">
17
+ <div class="progress-bar progress-bar-success" style="width: <%= (committed_p - used_p).to_i %>%;">
18
18
  <span class="sr-only"><%= committed_p %> % used</span>
19
19
  </div>
20
20
  </div>
@@ -151,8 +151,8 @@
151
151
 
152
152
  <% if targets.size > 0 %>
153
153
  <table class="table">
154
- <tr><th>Target</th><th>Auto field</th><th>Fields</th><th></th><th>modified</th></tr>
155
- <% targets.each do |target| %>
154
+ <tr><th>Target</th><th>Auto field</th><th>Fields</th><th>modified</th><th></th></tr>
155
+ <% targets.each_with_index do |target, index| %>
156
156
  <tr>
157
157
  <td><%= target[:name] %></td>
158
158
  <td><%= target[:auto_field] %></td>
@@ -163,11 +163,44 @@
163
163
  (lazy target)
164
164
  <% end %>
165
165
  </td>
166
- <td style="font-size: small; color: #c0c0c0;">TODO: change auto_fields, reserve fields, remove targets</td>
167
166
  <td style="text-align: right;"><%= target[:modified] %></td>
167
+ <td style="font-size: small; color: #c0c0c0;">
168
+ <a class="btn btn-danger btn-xs" data-toggle="modal" href="#removeTarget<%= index %>">
169
+ <span class="glyphicon glyphicon-trash"></span>
170
+ </a>
171
+ <div class="modal fade"
172
+ id="removeTarget<%= index %>"
173
+ tabindex="-1" role="dialog" aria-labelledby="removeTargetLabel<%= index %>" aria-hidden="true">
174
+ <div class="modal-dialog">
175
+ <div class="modal-content">
176
+ <div class="modal-header">
177
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
178
+ <h4 class="modal-title">Remove Target <%= target[:name] %></h4>
179
+ </div>
180
+ <div class="modal-body">
181
+ <p>Remove all definitions and queries?</p>
182
+ <p>name: <%= target[:name] %></p>
183
+ </div>
184
+ <div class="modal-footer">
185
+ <form class="form-inline" action="/close" method="POST">
186
+ <input type="hidden" name="target" value="<%= target[:name] %>" />
187
+ <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
188
+ <button type="submit" class="btn btn-danger">Remove</button>
189
+ </form>
190
+ </div>
191
+ </div><!-- /.modal-content -->
192
+ </div><!-- /.modal-dialog -->
193
+ </div><!-- /.modal -->
194
+ </td>
168
195
  </tr>
169
196
  <% end %>
170
197
  </table>
171
198
  <% else %>
172
199
  <p>No targets found.</p>
173
200
  <% end %>
201
+
202
+ <div class="page-header">
203
+ <h1 id="stats">Target/Query stat dump</h1>
204
+ </div>
205
+
206
+ <a href="/stat/dump" class="btn btn-info btn-lg" role="button">Download JSON</a>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: norikra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: java
6
6
  authors:
7
7
  - TAGOMORI Satoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-07 00:00:00.000000000 Z
11
+ date: 2014-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mizuno
@@ -292,6 +292,7 @@ files:
292
292
  - lib/norikra/query/ast.rb
293
293
  - lib/norikra/rpc.rb
294
294
  - lib/norikra/rpc/error.rb
295
+ - lib/norikra/rpc/gatekeeper.rb
295
296
  - lib/norikra/rpc/handler.rb
296
297
  - lib/norikra/rpc/http.rb
297
298
  - lib/norikra/rubyudf.rb