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