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.
- checksums.yaml +4 -4
- data/lib/delfos.rb +72 -13
- data/lib/delfos/call_stack.rb +9 -9
- data/lib/delfos/call_stack/stack.rb +18 -18
- data/lib/delfos/file_system.rb +16 -0
- data/lib/delfos/file_system/app_directories.rb +42 -0
- data/lib/delfos/file_system/common_path.rb +17 -11
- data/lib/delfos/file_system/distance_calculation.rb +12 -3
- data/lib/delfos/file_system/path_determination.rb +4 -1
- data/lib/delfos/file_system/relation.rb +1 -0
- data/lib/delfos/method_trace.rb +59 -0
- data/lib/delfos/method_trace/call_handler.rb +48 -0
- data/lib/delfos/method_trace/code_location.rb +35 -0
- data/lib/delfos/method_trace/code_location/call_site.rb +38 -0
- data/lib/delfos/method_trace/code_location/container_method_factory.rb +71 -0
- data/lib/delfos/method_trace/code_location/eval_in_caller.rb +17 -0
- data/lib/delfos/method_trace/code_location/filename_helpers.rb +46 -0
- data/lib/delfos/method_trace/code_location/method.rb +58 -0
- data/lib/delfos/neo4j.rb +4 -4
- data/lib/delfos/neo4j/batch/execution.rb +30 -88
- data/lib/delfos/neo4j/batch/retryable.rb +101 -0
- data/lib/delfos/neo4j/call_site_query.rb +89 -0
- data/lib/delfos/neo4j/distance/call_site_fetcher.rb +1 -0
- data/lib/delfos/neo4j/distance/update.rb +22 -5
- data/lib/delfos/neo4j/live/call_site_logger.rb +17 -0
- data/lib/delfos/neo4j/offline/call_site_logger.rb +39 -0
- data/lib/delfos/neo4j/offline/importer.rb +68 -0
- data/lib/delfos/neo4j/query_execution/errors.rb +13 -3
- data/lib/delfos/neo4j/query_execution/http.rb +1 -0
- data/lib/delfos/neo4j/query_execution/http_query.rb +20 -15
- data/lib/delfos/neo4j/query_execution/sync.rb +1 -0
- data/lib/delfos/neo4j/query_execution/transactional.rb +24 -13
- data/lib/delfos/neo4j/schema.rb +5 -19
- data/lib/delfos/setup.rb +76 -44
- metadata +77 -18
- data/lib/delfos/method_logging.rb +0 -52
- data/lib/delfos/method_logging/call_site_parsing.rb +0 -74
- data/lib/delfos/method_logging/code_location.rb +0 -89
- data/lib/delfos/method_logging/method_parameters.rb +0 -53
- data/lib/delfos/neo4j/call_stack_query.rb +0 -88
- data/lib/delfos/neo4j/informer.rb +0 -128
- data/lib/delfos/patching/basic_object.rb +0 -14
- data/lib/delfos/patching/basic_object_remove.rb +0 -7
- data/lib/delfos/patching/method_cache.rb +0 -83
- data/lib/delfos/patching/method_override.rb +0 -148
- data/lib/delfos/patching/module_defining_methods.rb +0 -105
- 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,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
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
|
-
|
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::
|
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
|
29
|
+
e.select { |k, _| %w[code message].include?(k) }
|
20
30
|
end.join("\n")
|
21
31
|
|
22
|
-
super [message, { query: query, params: params }
|
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
|
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":
|
26
|
+
@request_body ||= {
|
27
|
+
"statements": [{ "statement": formatted_query, "parameters": params }],
|
23
28
|
}.to_json
|
24
29
|
end
|
25
30
|
|
26
|
-
def
|
27
|
-
|
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
|
-
|
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
|
-
|
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
|