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