delfos 0.0.1 → 0.0.2.pre.rc2

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.
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