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,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delfos/call_stack"
|
4
|
+
require_relative "code_location"
|
5
|
+
require_relative "code_location/filename_helpers"
|
6
|
+
|
7
|
+
module Delfos
|
8
|
+
module MethodTrace
|
9
|
+
CallHandler = Struct.new(:trace_point)
|
10
|
+
|
11
|
+
class CallHandler
|
12
|
+
include CodeLocation::FilenameHelpers
|
13
|
+
|
14
|
+
def perform
|
15
|
+
return unless relevant?
|
16
|
+
|
17
|
+
CallStack.push(call_site)
|
18
|
+
end
|
19
|
+
|
20
|
+
def call_site
|
21
|
+
@call_site ||= CodeLocation.callsite_from(
|
22
|
+
container_method: container_method,
|
23
|
+
called_method: called_method,
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def container_method
|
28
|
+
@container_method ||= CodeLocation.create_container_method
|
29
|
+
end
|
30
|
+
|
31
|
+
def called_method
|
32
|
+
@called_method ||= CodeLocation.method_from(
|
33
|
+
object: trace_point.self,
|
34
|
+
method_name: trace_point.method_id,
|
35
|
+
file: trace_point.path,
|
36
|
+
line_number: trace_point.lineno,
|
37
|
+
class_method: trace_point.self.is_a?(Module),
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def relevant?
|
44
|
+
Delfos.include_file?(call_site.called_method_path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "code_location/method"
|
4
|
+
require_relative "code_location/call_site"
|
5
|
+
require_relative "code_location/container_method_factory"
|
6
|
+
require_relative "code_location/eval_in_caller"
|
7
|
+
|
8
|
+
module Delfos
|
9
|
+
module MethodTrace
|
10
|
+
module CodeLocation
|
11
|
+
class << self
|
12
|
+
include EvalInCaller
|
13
|
+
|
14
|
+
def method_from(attrs)
|
15
|
+
Method.new(attrs)
|
16
|
+
end
|
17
|
+
|
18
|
+
STACK_OFFSET = 8
|
19
|
+
|
20
|
+
def callsite_from(container_method:, called_method:, stack_offset: STACK_OFFSET)
|
21
|
+
CallSite.new(
|
22
|
+
container_method: container_method,
|
23
|
+
called_method: called_method,
|
24
|
+
file: eval_in_caller("__FILE__", stack_offset),
|
25
|
+
line_number: eval_in_caller("__LINE__", stack_offset),
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_container_method
|
30
|
+
ContainerMethodFactory.create
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "filename_helpers"
|
4
|
+
|
5
|
+
module Delfos
|
6
|
+
module MethodTrace
|
7
|
+
module CodeLocation
|
8
|
+
class CallSite
|
9
|
+
include FilenameHelpers
|
10
|
+
attr_reader :line_number, :container_method, :called_method
|
11
|
+
|
12
|
+
def initialize(file:, line_number:, container_method:, called_method:)
|
13
|
+
@file = file
|
14
|
+
@line_number = line_number.to_i
|
15
|
+
|
16
|
+
@container_method = container_method
|
17
|
+
@called_method = called_method
|
18
|
+
end
|
19
|
+
|
20
|
+
def container_method_path
|
21
|
+
container_method.raw_path
|
22
|
+
end
|
23
|
+
|
24
|
+
def called_method_path
|
25
|
+
called_method.raw_path
|
26
|
+
end
|
27
|
+
|
28
|
+
def summary
|
29
|
+
{
|
30
|
+
call_site: "#{file}:#{line_number}",
|
31
|
+
container_method: container_method.summary,
|
32
|
+
called_method: called_method.summary,
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "eval_in_caller"
|
4
|
+
require_relative "filename_helpers"
|
5
|
+
|
6
|
+
module Delfos
|
7
|
+
module MethodTrace
|
8
|
+
module CodeLocation
|
9
|
+
class ContainerMethodFactory
|
10
|
+
include EvalInCaller
|
11
|
+
include FilenameHelpers
|
12
|
+
STACK_OFFSET = 12
|
13
|
+
attr_reader :stack_offset
|
14
|
+
|
15
|
+
def self.create(stack_offset: STACK_OFFSET)
|
16
|
+
new(stack_offset: stack_offset).create
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(stack_offset:)
|
20
|
+
@stack_offset = stack_offset
|
21
|
+
end
|
22
|
+
|
23
|
+
def create
|
24
|
+
# ensure evaluated and memoised with correct stack offset
|
25
|
+
class_method
|
26
|
+
|
27
|
+
CodeLocation.method_from(
|
28
|
+
object: object,
|
29
|
+
method_name: meth,
|
30
|
+
file: file,
|
31
|
+
line_number: line,
|
32
|
+
class_method: class_method,
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def object
|
39
|
+
@object ||= eval_in_caller("self", stack_offset)
|
40
|
+
end
|
41
|
+
|
42
|
+
def class_method
|
43
|
+
return @class_method if defined?(@class_method)
|
44
|
+
|
45
|
+
@class_method = eval_in_caller("is_a?(Module)", STACK_OFFSET)
|
46
|
+
end
|
47
|
+
|
48
|
+
RUBY_IS_MAIN = "self.class == Object && self&.to_s == 'main' && __method__.nil?"
|
49
|
+
RUBY_SOURCE_LOCATION = "(__method__).source_location if __method__"
|
50
|
+
RUBY_CLASS_METHOD_SOURCE = "method#{RUBY_SOURCE_LOCATION}"
|
51
|
+
RUBY_INSTANCE_METHOD_SOURCE = "self.class.instance_method#{RUBY_SOURCE_LOCATION}"
|
52
|
+
|
53
|
+
def method_finder
|
54
|
+
@method_finder ||= class_method ? RUBY_CLASS_METHOD_SOURCE : RUBY_INSTANCE_METHOD_SOURCE
|
55
|
+
end
|
56
|
+
|
57
|
+
def file
|
58
|
+
@file ||= eval_in_caller("(#{RUBY_IS_MAIN}) ? __FILE__ : ((#{method_finder})&.first)", stack_offset)
|
59
|
+
end
|
60
|
+
|
61
|
+
def line
|
62
|
+
@line ||= eval_in_caller("(#{RUBY_IS_MAIN}) ? __LINE__ : ((#{method_finder})&.last)", stack_offset)
|
63
|
+
end
|
64
|
+
|
65
|
+
def meth
|
66
|
+
@meth ||= eval_in_caller("__method__", stack_offset)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "binding_of_caller"
|
4
|
+
|
5
|
+
module Delfos
|
6
|
+
module MethodTrace
|
7
|
+
module CodeLocation
|
8
|
+
module EvalInCaller
|
9
|
+
def eval_in_caller(s, offset, &block)
|
10
|
+
other = binding.of_caller(offset)
|
11
|
+
|
12
|
+
other.eval(s)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Delfos
|
4
|
+
module MethodTrace
|
5
|
+
module CodeLocation
|
6
|
+
module FilenameHelpers
|
7
|
+
def path
|
8
|
+
file
|
9
|
+
end
|
10
|
+
|
11
|
+
def file
|
12
|
+
relative_filename(@file)
|
13
|
+
end
|
14
|
+
|
15
|
+
def raw_path
|
16
|
+
@file
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def relative_filename(f)
|
22
|
+
return unless f
|
23
|
+
file = f.to_s
|
24
|
+
|
25
|
+
Delfos.application_directories.map do |d|
|
26
|
+
file = relative_path(file, d)
|
27
|
+
end
|
28
|
+
|
29
|
+
file
|
30
|
+
end
|
31
|
+
|
32
|
+
def relative_path(file, dir)
|
33
|
+
dir = File.expand_path(dir)
|
34
|
+
match = dir.to_s.split("/")[0..-2].join("/")
|
35
|
+
|
36
|
+
if match.length.positive? && file[match]
|
37
|
+
file = file.gsub(match, "").
|
38
|
+
gsub(%r{^/}, "")
|
39
|
+
end
|
40
|
+
|
41
|
+
file
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "filename_helpers"
|
4
|
+
|
5
|
+
module Delfos
|
6
|
+
module MethodTrace
|
7
|
+
module CodeLocation
|
8
|
+
class Method
|
9
|
+
include FilenameHelpers
|
10
|
+
attr_reader :object, :line_number, :class_method
|
11
|
+
|
12
|
+
def initialize(object:, method_name:, file:, line_number:, class_method:)
|
13
|
+
@object = object
|
14
|
+
@method_name = method_name
|
15
|
+
@file = file
|
16
|
+
@line_number = line_number
|
17
|
+
@class_method = class_method
|
18
|
+
end
|
19
|
+
|
20
|
+
def klass_name
|
21
|
+
klass.name
|
22
|
+
end
|
23
|
+
|
24
|
+
def klass
|
25
|
+
object.is_a?(Module) ? object : object.class
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_name
|
29
|
+
(@method_name || "(main)").to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_type
|
33
|
+
class_method ? "ClassMethod" : "InstanceMethod"
|
34
|
+
end
|
35
|
+
|
36
|
+
def summary(reverse: false)
|
37
|
+
summary = [source_location, method_summary]
|
38
|
+
|
39
|
+
(reverse ? summary.reverse : summary).join " "
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def method_summary
|
45
|
+
"#{klass}#{separator}#{method_name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def source_location
|
49
|
+
"#{file}:#{line_number}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def separator
|
53
|
+
class_method ? "." : "#"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/delfos/neo4j.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
3
|
require_relative "neo4j/query_execution/sync"
|
4
|
-
require_relative "neo4j/batch/
|
4
|
+
require_relative "neo4j/batch/retryable"
|
5
5
|
require_relative "neo4j/schema"
|
6
6
|
require_relative "neo4j/distance/update"
|
7
7
|
|
@@ -14,11 +14,11 @@ module Delfos
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def execute(query, params = {})
|
17
|
-
Batch::
|
17
|
+
Batch::Retryable.execute!(query, params: params, size: Delfos.batch_size)
|
18
18
|
end
|
19
19
|
|
20
20
|
def flush!
|
21
|
-
Batch::
|
21
|
+
Batch::Retryable.flush!
|
22
22
|
end
|
23
23
|
|
24
24
|
def ensure_schema!
|
@@ -1,103 +1,42 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "delfos/neo4j/query_execution/transactional"
|
3
4
|
|
4
5
|
module Delfos
|
5
6
|
module Neo4j
|
6
7
|
module Batch
|
7
8
|
class Execution
|
8
|
-
|
9
|
-
|
10
|
-
class << self
|
11
|
-
def execute!(query, params: {}, size: nil)
|
12
|
-
batch = @batch || new_batch(size || 1_000)
|
13
|
-
|
14
|
-
batch.execute!(query, params: params)
|
15
|
-
end
|
16
|
-
|
17
|
-
def new_batch(size)
|
18
|
-
@batch = new(size: size)
|
19
|
-
end
|
20
|
-
|
21
|
-
def flush!
|
22
|
-
@batch&.flush!
|
23
|
-
rescue
|
24
|
-
reset!
|
25
|
-
end
|
26
|
-
|
27
|
-
def reset!
|
28
|
-
@batch = nil
|
29
|
-
end
|
30
|
-
|
31
|
-
attr_writer :batch
|
32
|
-
end
|
9
|
+
attr_reader :size, :current_transaction_url, :commit_url, :expires, :query_count
|
33
10
|
|
34
11
|
def initialize(size:, clock: Time)
|
35
|
-
@size
|
36
|
-
@clock
|
37
|
-
|
38
|
-
@expires = nil
|
39
|
-
@commit_url = nil
|
40
|
-
@current_transaction_url = nil
|
12
|
+
@size = size
|
13
|
+
@clock = clock
|
14
|
+
reset!
|
41
15
|
end
|
42
16
|
|
43
|
-
|
44
|
-
|
45
|
-
def execute!(query, params: {}, retrying: false)
|
46
|
-
queries.push([query, params]) unless retrying
|
47
|
-
|
48
|
-
with_retry(retrying) do
|
49
|
-
BATCH_MUTEX.synchronize do
|
50
|
-
check_for_expiry!
|
17
|
+
def execute!(query, params: {})
|
18
|
+
check_for_expiry!
|
51
19
|
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
end
|
20
|
+
perform_query(query, params)
|
21
|
+
flush_if_required!
|
56
22
|
end
|
57
23
|
|
58
24
|
def flush!
|
59
25
|
return unless query_count.positive?
|
60
26
|
return unless @commit_url
|
61
|
-
QueryExecution::Transactional.
|
27
|
+
QueryExecution::Transactional.commit!(@commit_url)
|
62
28
|
|
63
29
|
reset!
|
64
30
|
end
|
65
31
|
|
66
|
-
def query_count
|
67
|
-
queries.length
|
68
|
-
end
|
69
|
-
|
70
32
|
private
|
71
33
|
|
72
34
|
def perform_query(query, params)
|
73
35
|
transactional_query = QueryExecution::Transactional.new(query, params, url)
|
36
|
+
@total_query_length += transactional_query.query_length
|
74
37
|
transaction_url, @commit_url, @expires = transactional_query.perform
|
75
38
|
@current_transaction_url ||= transaction_url # the transaction_url is only returned with the first request
|
76
|
-
|
77
|
-
|
78
|
-
def with_retry(retrying)
|
79
|
-
yield
|
80
|
-
rescue QueryExecution::ExpiredTransaction
|
81
|
-
@retry_count ||= 0
|
82
|
-
|
83
|
-
if retrying
|
84
|
-
@retry_count += 1
|
85
|
-
|
86
|
-
if @retry_count > 5
|
87
|
-
@retry_count = 0
|
88
|
-
raise
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
Delfos.logger.error "Transaction expired - retrying batch. #{query_count} queries retry_count: #{@retry_count}"
|
93
|
-
reset_transaction!
|
94
|
-
retry_batch!
|
95
|
-
end
|
96
|
-
|
97
|
-
def retry_batch!
|
98
|
-
queries.each do |q, p|
|
99
|
-
execute!(q, params: p, retrying: true)
|
100
|
-
end
|
39
|
+
@query_count += 1
|
101
40
|
end
|
102
41
|
|
103
42
|
def url
|
@@ -110,19 +49,21 @@ module Delfos
|
|
110
49
|
Delfos.neo4j.uri_for("/db/data/transaction")
|
111
50
|
end
|
112
51
|
|
113
|
-
def
|
114
|
-
|
52
|
+
def flush_if_required!
|
53
|
+
check_for_expiry!
|
115
54
|
|
116
|
-
if
|
117
|
-
|
118
|
-
|
55
|
+
if batch_full? || expires_soon? || large_query?
|
56
|
+
flush!
|
57
|
+
return true
|
119
58
|
end
|
59
|
+
|
60
|
+
false
|
120
61
|
end
|
121
62
|
|
122
|
-
def
|
123
|
-
|
63
|
+
def check_for_expiry!
|
64
|
+
return if @expires.nil? || (@clock.now <= @expires)
|
124
65
|
|
125
|
-
|
66
|
+
raise QueryExecution::ExpiredTransaction.new(@commit_url, "")
|
126
67
|
end
|
127
68
|
|
128
69
|
def batch_full?
|
@@ -130,18 +71,19 @@ module Delfos
|
|
130
71
|
end
|
131
72
|
|
132
73
|
def expires_soon?
|
133
|
-
@expires && (@clock.now +
|
74
|
+
@expires && (@clock.now + 10 > @expires)
|
134
75
|
end
|
135
76
|
|
136
|
-
def
|
137
|
-
@
|
138
|
-
reset_transaction!
|
77
|
+
def large_query?
|
78
|
+
@total_query_length >= Delfos.max_query_size
|
139
79
|
end
|
140
80
|
|
141
|
-
def
|
81
|
+
def reset!
|
82
|
+
@query_count = 0
|
83
|
+
@total_query_length = 0
|
142
84
|
@current_transaction_url = nil
|
143
|
-
@commit_url
|
144
|
-
@expires
|
85
|
+
@commit_url = nil
|
86
|
+
@expires = nil
|
145
87
|
end
|
146
88
|
end
|
147
89
|
end
|