delfos 0.0.1 → 0.0.2.pre.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/delfos.rb +72 -13
  3. data/lib/delfos/call_stack.rb +9 -9
  4. data/lib/delfos/call_stack/stack.rb +18 -18
  5. data/lib/delfos/file_system.rb +16 -0
  6. data/lib/delfos/file_system/app_directories.rb +42 -0
  7. data/lib/delfos/file_system/common_path.rb +17 -11
  8. data/lib/delfos/file_system/distance_calculation.rb +12 -3
  9. data/lib/delfos/file_system/path_determination.rb +4 -1
  10. data/lib/delfos/file_system/relation.rb +1 -0
  11. data/lib/delfos/method_trace.rb +59 -0
  12. data/lib/delfos/method_trace/call_handler.rb +48 -0
  13. data/lib/delfos/method_trace/code_location.rb +35 -0
  14. data/lib/delfos/method_trace/code_location/call_site.rb +38 -0
  15. data/lib/delfos/method_trace/code_location/container_method_factory.rb +71 -0
  16. data/lib/delfos/method_trace/code_location/eval_in_caller.rb +17 -0
  17. data/lib/delfos/method_trace/code_location/filename_helpers.rb +46 -0
  18. data/lib/delfos/method_trace/code_location/method.rb +58 -0
  19. data/lib/delfos/neo4j.rb +4 -4
  20. data/lib/delfos/neo4j/batch/execution.rb +30 -88
  21. data/lib/delfos/neo4j/batch/retryable.rb +101 -0
  22. data/lib/delfos/neo4j/call_site_query.rb +89 -0
  23. data/lib/delfos/neo4j/distance/call_site_fetcher.rb +1 -0
  24. data/lib/delfos/neo4j/distance/update.rb +22 -5
  25. data/lib/delfos/neo4j/live/call_site_logger.rb +17 -0
  26. data/lib/delfos/neo4j/offline/call_site_logger.rb +39 -0
  27. data/lib/delfos/neo4j/offline/importer.rb +68 -0
  28. data/lib/delfos/neo4j/query_execution/errors.rb +13 -3
  29. data/lib/delfos/neo4j/query_execution/http.rb +1 -0
  30. data/lib/delfos/neo4j/query_execution/http_query.rb +20 -15
  31. data/lib/delfos/neo4j/query_execution/sync.rb +1 -0
  32. data/lib/delfos/neo4j/query_execution/transactional.rb +24 -13
  33. data/lib/delfos/neo4j/schema.rb +5 -19
  34. data/lib/delfos/setup.rb +76 -44
  35. metadata +77 -18
  36. data/lib/delfos/method_logging.rb +0 -52
  37. data/lib/delfos/method_logging/call_site_parsing.rb +0 -74
  38. data/lib/delfos/method_logging/code_location.rb +0 -89
  39. data/lib/delfos/method_logging/method_parameters.rb +0 -53
  40. data/lib/delfos/neo4j/call_stack_query.rb +0 -88
  41. data/lib/delfos/neo4j/informer.rb +0 -128
  42. data/lib/delfos/patching/basic_object.rb +0 -14
  43. data/lib/delfos/patching/basic_object_remove.rb +0 -7
  44. data/lib/delfos/patching/method_cache.rb +0 -83
  45. data/lib/delfos/patching/method_override.rb +0 -148
  46. data/lib/delfos/patching/module_defining_methods.rb +0 -105
  47. data/lib/delfos/patching/unstubber.rb +0 -34
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "execution"
4
+
5
+ module Delfos
6
+ module Neo4j
7
+ module Batch
8
+ class Retryable
9
+ class << self
10
+ MUTEX = Mutex.new
11
+
12
+ def reset!
13
+ MUTEX.synchronize do
14
+ Thread.current[:_delfos__retryable_batch] = nil
15
+ end
16
+ end
17
+
18
+ def ensure_instance(size: nil)
19
+ MUTEX.synchronize do
20
+ Thread.current[:_delfos__retryable_batch] ||= new(size: size || 1_000)
21
+ end
22
+ end
23
+
24
+ def execute!(query, params: {}, size: nil)
25
+ ensure_instance(size: size).execute!(query, params: params)
26
+ end
27
+
28
+ def flush!
29
+ ensure_instance&.flush!
30
+ reset!
31
+ end
32
+ end
33
+
34
+ attr_reader :size, :queries, :execution
35
+ attr_accessor :retry_count
36
+
37
+ def initialize(size:)
38
+ @size = size
39
+ reset!
40
+ end
41
+
42
+ def execute!(query, params: {}, retrying: false)
43
+ queries.push([query, params]) unless retrying
44
+
45
+ with_retry(retrying) do
46
+ flushed = execution.execute!(query, params: params)
47
+
48
+ reset! if flushed
49
+ end
50
+ end
51
+
52
+ def flush!
53
+ execution.flush!
54
+ reset!
55
+ end
56
+
57
+ private
58
+
59
+ def with_retry(already_retrying)
60
+ yield
61
+ rescue QueryExecution::ExpiredTransaction
62
+ check_retry_limit! if already_retrying
63
+
64
+ retry_batch!
65
+ end
66
+
67
+ def check_retry_limit!
68
+ self.retry_count += 1
69
+
70
+ return if self.retry_count <= 5
71
+
72
+ self.retry_count = 0
73
+ Delfos.logger.error "Transaction expired - 5 retries failed aborting"
74
+ raise
75
+ end
76
+
77
+ def retry_batch!
78
+ Delfos.logger.error do
79
+ "Transaction expired - retrying batch. #{queries.count} queries retry_count: #{retry_count}"
80
+ end
81
+
82
+ new_execution
83
+
84
+ queries.each { |q, p| execute!(q, params: p, retrying: true) }
85
+
86
+ Delfos.logger.error { "Batch retry successful" }
87
+ end
88
+
89
+ def reset!
90
+ @queries = []
91
+ new_execution
92
+ @retry_count = 0
93
+ end
94
+
95
+ def new_execution
96
+ @execution = Execution.new(size: size)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Delfos
4
+ module Neo4j
5
+ class CallSiteQuery
6
+ def initialize(call_site, stack_uuid, step_number)
7
+ @call_site = call_site
8
+ @container_method = call_site.container_method
9
+ @called_method = call_site.called_method
10
+ @stack_uuid = stack_uuid
11
+ @step_number = step_number
12
+ end
13
+
14
+ def params
15
+ @params ||= calculate_params
16
+ end
17
+
18
+ def query
19
+ BODY
20
+ end
21
+
22
+ private
23
+
24
+ # rubocop:disable Metrics/MethodLength
25
+ def calculate_params
26
+ params = {
27
+ "step_number" => @step_number,
28
+ "stack_uuid" => @stack_uuid,
29
+
30
+ "call_site_file" => @call_site .file,
31
+ "call_site_line_number" => @call_site .line_number,
32
+
33
+ "container_method_klass_name" => @container_method .klass_name,
34
+ "container_method_type" => @container_method .method_type,
35
+ "container_method_name" => @container_method .method_name,
36
+ "container_method_file" => @container_method .file,
37
+ "container_method_line_number" => @container_method .line_number || -1,
38
+
39
+ "called_method_klass_name" => @called_method .klass_name,
40
+ "called_method_type" => @called_method .method_type,
41
+ "called_method_name" => @called_method .method_name,
42
+ "called_method_file" => @called_method .file,
43
+ "called_method_line_number" => @called_method .line_number,
44
+ }
45
+
46
+ params
47
+ end
48
+ # rubocop:enable Metrics/MethodLength,Metrics/LineLength,Metrics/AbcSize
49
+
50
+ BODY = <<-QUERY
51
+ MERGE (container_method_klass:Class {name: {container_method_klass_name}})
52
+ MERGE (called_method_klass:Class {name: {called_method_klass_name}})
53
+
54
+ MERGE (container_method_klass) - [:OWNS] ->
55
+ (container_method:Method
56
+ {
57
+ type: {container_method_type},
58
+ name: {container_method_name},
59
+ file: {container_method_file},
60
+ line_number: {container_method_line_number}
61
+ }
62
+ )
63
+ MERGE (container_method) - [:CONTAINS] ->
64
+ (call_site:CallSite
65
+ {
66
+ file: {call_site_file},
67
+ line_number: {call_site_line_number}
68
+ }
69
+ )
70
+
71
+ MERGE (called_method_klass) - [:OWNS] ->
72
+ (called_method:Method
73
+ {
74
+ type: {called_method_type},
75
+ name: {called_method_name},
76
+ file: {called_method_file},
77
+ line_number: {called_method_line_number}
78
+ }
79
+ )
80
+
81
+ MERGE (call_site) - [:CALLS] -> (called_method)
82
+
83
+ MERGE (call_stack:CallStack{uuid: {stack_uuid}})
84
+
85
+ MERGE (call_stack) - [:STEP {number: {step_number}}] -> (call_site)
86
+ QUERY
87
+ end
88
+ end
89
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Delfos
3
4
  module Neo4j
4
5
  module Distance
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require "delfos/file_system/distance_calculation"
2
+
3
+ require "delfos/file_system"
3
4
  require_relative "call_site_fetcher"
4
5
 
5
6
  module Delfos
@@ -11,9 +12,7 @@ module Delfos
11
12
  return if results.length.negative?
12
13
 
13
14
  results.each do |start_file, call_site_id, finish_file, called_id|
14
- calc = FileSystem::DistanceCalculation.new(start_file, finish_file)
15
-
16
- update(call_site_id, called_id, calc)
15
+ handle(start_file, call_site_id, finish_file, called_id)
17
16
  end
18
17
 
19
18
  Neo4j.flush!
@@ -25,7 +24,7 @@ module Delfos
25
24
  call_site_id: call_site_id,
26
25
  called_id: called_id,
27
26
  sum_traversals: calc.sum_traversals,
28
- sum_possible_traversals: calc.sum_possible_traversals
27
+ sum_possible_traversals: calc.sum_possible_traversals,
29
28
  )
30
29
  end
31
30
 
@@ -44,6 +43,24 @@ module Delfos
44
43
  -> (called)
45
44
  QUERY
46
45
  end
46
+
47
+ private
48
+
49
+ def handle(start_file, call_site_id, finish_file, called_id)
50
+ calc = FileSystem.distance_calculation(start_file, finish_file)
51
+
52
+ update(call_site_id, called_id, calc)
53
+ rescue FileSystem::Error => e
54
+ error(e, start_file, call_site_id, finish_file, called_id)
55
+ end
56
+
57
+ def error(e, start_file, call_site_id, finish_file, called_id)
58
+ Delfos.logger.error([
59
+ "Whilst updating distance #{start_file}->#{finish_file} ",
60
+ "call_site_id: #{call_site_id}",
61
+ "called_id: #{called_id} - #{e.message} #{e.backtrace}",
62
+ ].join("\n"))
63
+ end
47
64
  end
48
65
  end
49
66
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delfos/neo4j/call_site_query"
4
+
5
+ module Delfos
6
+ module Neo4j
7
+ module Live
8
+ class CallSiteLogger
9
+ def log(call_site, stack_uuid, step_number)
10
+ q = CallSiteQuery.new(call_site, stack_uuid, step_number)
11
+
12
+ Neo4j.execute(q.query, q.params)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delfos/neo4j/call_site_query"
4
+ require "json"
5
+
6
+ module Delfos
7
+ module Neo4j
8
+ module Offline
9
+ class CallSiteLogger
10
+ attr_writer :count
11
+
12
+ def log(call_site, stack_uuid, step_number)
13
+ query = CallSiteQuery.new(call_site, stack_uuid, step_number)
14
+
15
+ file.puts JSON.dump(query.params)
16
+ self.count += 1
17
+ file.flush if (count % 100).zero?
18
+ end
19
+
20
+ def finish!
21
+ return if file.closed?
22
+
23
+ file.flush
24
+ file.close
25
+ end
26
+
27
+ def count
28
+ @count ||= 0
29
+ end
30
+
31
+ private
32
+
33
+ def file
34
+ @file ||= File.open(Delfos.offline_query_filename, "a")
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "delfos/neo4j"
5
+ require "delfos/neo4j/call_site_query"
6
+ require "fileutils"
7
+
8
+ module Delfos
9
+ module Neo4j
10
+ module Offline
11
+ class Importer
12
+ attr_reader :filename
13
+
14
+ def initialize(filename)
15
+ @filename = filename
16
+ end
17
+
18
+ def perform
19
+ each_line do |params, err|
20
+ params = JSON.parse(params)
21
+
22
+ execute(CallSiteQuery::BODY, params, err)
23
+ end
24
+
25
+ Neo4j.flush!
26
+ end
27
+
28
+ private
29
+
30
+ def execute(query, params, err)
31
+ Neo4j.execute_sync(query, params)
32
+ rescue Delfos::Neo4j::QueryExecution::InvalidQuery => e
33
+ @no_errors = false
34
+ Delfos.logger.error e.message.to_s
35
+ err.puts JSON.dump(params)
36
+ end
37
+
38
+ def each_line
39
+ with_errors do |err|
40
+ with_input do |f|
41
+ f.each_line.lazy.each do |params|
42
+ yield params, err
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def with_errors
49
+ @no_errors = true
50
+
51
+ error_filename = "#{filename}.errors"
52
+
53
+ File.open(error_filename, "w") do |err|
54
+ yield err
55
+ end
56
+
57
+ FileUtils.rm_rf error_filename if @no_errors
58
+ end
59
+
60
+ def with_input
61
+ File.open(filename, "r") do |f|
62
+ yield f
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,25 +1,35 @@
1
1
  # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+
2
5
  module Delfos
3
6
  module Neo4j
4
7
  module QueryExecution
5
8
  HTTP_ERRORS = [
6
9
  EOFError,
7
- Errno::ECONNRESET,
10
+ Errno::EAGAIN,
8
11
  Errno::ECONNREFUSED,
12
+ Errno::ECONNRESET,
13
+ Errno::EHOSTUNREACH,
9
14
  Errno::EINVAL,
15
+ Errno::ENETDOWN,
16
+ Errno::ENETUNREACH,
17
+ Errno::ETIMEDOUT,
10
18
  Net::HTTPBadResponse,
11
19
  Net::HTTPHeaderSyntaxError,
12
20
  Net::ProtocolError,
21
+ Net::ReadTimeout,
22
+ SocketError,
13
23
  Timeout::Error,
14
24
  ].freeze
15
25
 
16
26
  class InvalidQuery < IOError
17
27
  def initialize(errors, query, params)
18
28
  message = errors.map do |e|
19
- e.select { |k, _| %w(code message).include?(k) }.inspect
29
+ e.select { |k, _| %w[code message].include?(k) }
20
30
  end.join("\n")
21
31
 
22
- super [message, { query: query, params: params }.to_json].join("\n\n")
32
+ super [message, { query: query.to_s }, { params: params.to_s }].join("\n\n")
23
33
  end
24
34
  end
25
35
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "delfos"
3
4
  require "net/http"
4
5
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Delfos
3
4
  module Neo4j
4
5
  module QueryExecution
@@ -15,19 +16,20 @@ module Delfos
15
16
  @uri = uri
16
17
  end
17
18
 
19
+ def query_length
20
+ request_body.length
21
+ end
22
+
18
23
  private
19
24
 
20
25
  def request_body
21
- {
22
- "statements": [{ "statement": strip_whitespace(query), "parameters": params }],
26
+ @request_body ||= {
27
+ "statements": [{ "statement": formatted_query, "parameters": params }],
23
28
  }.to_json
24
29
  end
25
30
 
26
- def strip_whitespace(s)
27
- s.
28
- gsub(/^\s+/, "").
29
- gsub(/ +/, " ").
30
- gsub("\n\n", "\n")
31
+ def formatted_query
32
+ strip_whitespace(query)
31
33
  end
32
34
 
33
35
  def json
@@ -49,18 +51,21 @@ module Delfos
49
51
  end
50
52
 
51
53
  def log_query
52
- statement = strip_whitespace(query)
54
+ Delfos.logger.debug do
55
+ statement = formatted_query
53
56
 
54
- if statement
55
- begin
56
57
  params.each { |k, v| statement = statement.gsub("{#{k}}", v.inspect) }
57
- rescue
58
- byebug
59
- end
60
- Delfos.logger.debug "sending query: "
61
- Delfos.logger.debug statement.gsub(/^/, " ")
58
+ statement.gsub(/^/, " ")
62
59
  end
63
60
  end
61
+
62
+ def strip_whitespace(s)
63
+ s.
64
+ gsub(/^\s+/, "").
65
+ gsub(/ +/, " ").
66
+ gsub("\n\n", "\n").
67
+ tr("\n", " ")
68
+ end
64
69
  end
65
70
  end
66
71
  end