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,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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
- require_relative "neo4j/informer"
2
+
3
3
  require_relative "neo4j/query_execution/sync"
4
- require_relative "neo4j/batch/execution"
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::Execution.execute!(query, params: params)
17
+ Batch::Retryable.execute!(query, params: params, size: Delfos.batch_size)
18
18
  end
19
19
 
20
20
  def flush!
21
- Batch::Execution.flush!
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
- BATCH_MUTEX = Mutex.new
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 = size
36
- @clock = clock
37
- @queries = []
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
- attr_reader :size, :current_transaction_url, :commit_url, :expires, :queries
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
- perform_query(query, params)
53
- flush_if_required!
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.flush!(@commit_url)
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
- end
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 check_for_expiry!
114
- return unless @expires
52
+ def flush_if_required!
53
+ check_for_expiry!
115
54
 
116
- if @clock.now > @expires
117
- self.class.batch = nil
118
- raise QueryExecution::ExpiredTransaction.new(@comit_url, "")
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 flush_if_required!
123
- check_for_expiry!
63
+ def check_for_expiry!
64
+ return if @expires.nil? || (@clock.now <= @expires)
124
65
 
125
- flush! if batch_full? || expires_soon?
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 + 2 > @expires)
74
+ @expires && (@clock.now + 10 > @expires)
134
75
  end
135
76
 
136
- def reset!
137
- @queries = []
138
- reset_transaction!
77
+ def large_query?
78
+ @total_query_length >= Delfos.max_query_size
139
79
  end
140
80
 
141
- def reset_transaction!
81
+ def reset!
82
+ @query_count = 0
83
+ @total_query_length = 0
142
84
  @current_transaction_url = nil
143
- @commit_url = nil
144
- @expires = nil
85
+ @commit_url = nil
86
+ @expires = nil
145
87
  end
146
88
  end
147
89
  end