norikra 0.1.4-java → 0.1.5-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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