Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
b966a20f
Commit
b966a20f
authored
May 21, 2021
by
alinamihaila
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add metric suggested name
parent
ec2587c6
Changes
3
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
223 additions
and
177 deletions
+223
-177
lib/gitlab/usage/metrics/instrumentations/database_metric.rb
lib/gitlab/usage/metrics/instrumentations/database_metric.rb
+13
-1
lib/gitlab/usage/metrics/name_suggestion.rb
lib/gitlab/usage/metrics/name_suggestion.rb
+202
-0
lib/gitlab/usage/metrics/names_suggestions/generator.rb
lib/gitlab/usage/metrics/names_suggestions/generator.rb
+8
-176
No files found.
lib/gitlab/usage/metrics/instrumentations/database_metric.rb
View file @
b966a20f
# frozen_string_literal: true
1
# frozen_string_literal: true
module
Gitlab
module
Usage
...
...
@@ -47,6 +47,18 @@ module Gitlab
Gitlab
::
Usage
::
Metrics
::
Query
.
for
(
self
.
class
.
metric_operation
,
relation
,
self
.
class
.
column
)
end
def
suggested_name
Gitlab
::
Usage
::
Metrics
::
NameSuggestion
.
for
(
self
.
class
.
metric_operation
,
relation:
relation
,
column:
self
.
class
.
column
)
end
def
relation
self
.
class
.
metric_relation
.
call
.
where
(
time_constraints
)
end
private
def
relation
...
...
lib/gitlab/usage/metrics/name_suggestion.rb
0 → 100644
View file @
b966a20f
# frozen_string_literal: true
module
Gitlab
module
Usage
module
Metrics
class
NameSuggestion
FREE_TEXT_METRIC_NAME
=
"<please fill metric name>"
REDIS_EVENT_METRIC_NAME
=
"<please fill metric name, suggested format is: {subject}_{verb}{ing|ed}_{object} eg: users_creating_epics or merge_requests_viewed_in_single_file_mode>"
CONSTRAINTS_PROMPT_TEMPLATE
=
"<adjective describing: '%{constraints}'>"
class
<<
self
def
for
(
operation
,
relation:
nil
,
column:
nil
)
case
operation
when
:count
name_suggestion
(
column:
column
,
relation:
relation
,
prefix:
'count'
)
when
:distinct_count
name_suggestion
(
column:
column
,
relation:
relation
,
prefix:
'count_distinct'
,
distinct: :distinct
)
when
:estimate_batch_distinct_count
name_suggestion
(
column:
column
,
relation:
relation
,
prefix:
'estimate_distinct_count'
)
when
:add
"add_
#{
relation
.
join
(
'_and_'
)
}
"
when
:sum
name_suggestion
(
column:
column
,
relation:
relation
,
prefix:
'sum'
)
when
:redis
REDIS_EVENT_METRIC_NAME
when
:alt
FREE_TEXT_METRIC_NAME
else
raise
ArgumentError
,
"
#{
operation
}
operation not supported"
end
end
private
def
name_suggestion
(
relation
:,
column:
nil
,
prefix:
nil
,
distinct:
nil
)
# rubocop: disable CodeReuse/ActiveRecord
relation
=
relation
.
unscope
(
where: :created_at
)
# rubocop: enable CodeReuse/ActiveRecord
parts
=
[
prefix
]
arel_column
=
arelize_column
(
relation
,
column
)
# nil as column indicates that the counting would use fallback value of primary key.
# Because counting primary key from relation is the conceptual equal to counting all
# records from given relation, in order to keep name suggestion more condensed
# primary key column is skipped.
# eg: SELECT COUNT(id) FROM issues would translate as count_issues and not
# as count_id_from_issues since it does not add more information to the name suggestion
if
arel_column
!=
Arel
::
Table
.
new
(
relation
.
table_name
)[
relation
.
primary_key
]
parts
<<
arel_column
.
name
parts
<<
'from'
end
arel
=
arel_query
(
relation:
relation
,
column:
arel_column
,
distinct:
distinct
)
constraints
=
parse_constraints
(
relation:
relation
,
arel:
arel
)
# In some cases due to performance reasons metrics are instrumented with joined relations
# where relation listed in FROM statement is not the one that includes counted attribute
# in such situations to make name suggestion more intuitive source should be inferred based
# on the relation that provide counted attribute
# EG: SELECT COUNT(deployments.environment_id) FROM clusters
# JOIN deployments ON deployments.cluster_id = cluster.id
# should be translated into:
# count_environment_id_from_deployments_with_clusters
# instead of
# count_environment_id_from_clusters_with_deployments
actual_source
=
parse_source
(
relation
,
arel_column
)
append_constraints_prompt
(
actual_source
,
[
constraints
],
parts
)
parts
<<
actual_source
parts
+=
process_joined_relations
(
actual_source
,
arel
,
relation
,
constraints
)
parts
.
compact
.
join
(
'_'
).
delete
(
'"'
)
end
def
append_constraints_prompt
(
target
,
constraints
,
parts
)
applicable_constraints
=
constraints
.
select
{
|
constraint
|
constraint
.
include?
(
target
)
}
return
unless
applicable_constraints
.
any?
parts
<<
CONSTRAINTS_PROMPT_TEMPLATE
%
{
constraints:
applicable_constraints
.
join
(
' AND '
)
}
end
def
parse_constraints
(
relation
:,
arel
:)
connection
=
relation
.
connection
::
Gitlab
::
Usage
::
Metrics
::
NamesSuggestions
::
RelationParsers
::
Constraints
.
new
(
connection
)
.
accept
(
arel
,
collector
(
connection
))
.
value
end
# TODO: joins with `USING` keyword
def
process_joined_relations
(
actual_source
,
arel
,
relation
,
where_constraints
)
joins
=
parse_joins
(
connection:
relation
.
connection
,
arel:
arel
)
return
[]
unless
joins
.
any?
sources
=
[
relation
.
table_name
,
*
joins
.
map
{
|
join
|
join
[
:source
]
}]
joins
=
extract_joins_targets
(
joins
,
sources
)
relations
=
if
actual_source
!=
relation
.
table_name
build_relations_tree
(
joins
+
[{
source:
relation
.
table_name
}],
actual_source
)
else
# in case where counter attribute comes from joined relations, the relations
# diagram has to be built bottom up, thus source and target are reverted
build_relations_tree
(
joins
+
[{
source:
relation
.
table_name
}],
actual_source
,
source_key: :target
,
target_key: :source
)
end
collect_join_parts
(
relations:
relations
[
actual_source
],
joins:
joins
,
wheres:
where_constraints
)
end
def
parse_joins
(
connection
:,
arel
:)
::
Gitlab
::
Usage
::
Metrics
::
NamesSuggestions
::
RelationParsers
::
Joins
.
new
(
connection
)
.
accept
(
arel
)
end
def
extract_joins_targets
(
joins
,
sources
)
joins
.
map
do
|
join
|
source_regex
=
/(
#{
join
[
:source
]
}
)\.(\w+_)*id/i
tables_except_src
=
(
sources
-
[
join
[
:source
]]).
join
(
'|'
)
target_regex
=
/(?<target>
#{
tables_except_src
}
)\.(\w+_)*id/i
join_cond_regex
=
/(
#{
source_regex
}
\s+=\s+
#{
target_regex
}
)|(
#{
target_regex
}
\s+=\s+
#{
source_regex
}
)/i
matched
=
join_cond_regex
.
match
(
join
[
:constraints
])
if
matched
join
[
:target
]
=
matched
[
:target
]
join
[
:constraints
].
gsub!
(
/
#{
join_cond_regex
}
(\s+(and|or))*/i
,
''
)
end
join
end
end
def
build_relations_tree
(
joins
,
parent
,
source_key: :source
,
target_key: :target
)
return
[]
if
joins
.
blank?
tree
=
{}
tree
[
parent
]
=
[]
joins
.
each
do
|
join
|
if
join
[
source_key
]
==
parent
tree
[
parent
]
<<
build_relations_tree
(
joins
-
[
join
],
join
[
target_key
],
source_key:
source_key
,
target_key:
target_key
)
end
end
tree
end
def
collect_join_parts
(
relations
:,
joins
:,
wheres
:,
parts:
[],
conjunctions:
%w[with having including]
.
cycle
)
conjunction
=
conjunctions
.
next
relations
.
each
do
|
subtree
|
subtree
.
each
do
|
parent
,
children
|
parts
<<
"<
#{
conjunction
}
>"
join_constraints
=
joins
.
find
{
|
join
|
join
[
:source
]
==
parent
}
&
.
dig
(
:constraints
)
append_constraints_prompt
(
parent
,
[
wheres
,
join_constraints
].
compact
,
parts
)
parts
<<
parent
collect_join_parts
(
relations:
children
,
joins:
joins
,
wheres:
wheres
,
parts:
parts
,
conjunctions:
conjunctions
)
end
end
parts
end
def
arelize_column
(
relation
,
column
)
case
column
when
Arel
::
Attribute
column
when
NilClass
Arel
::
Table
.
new
(
relation
.
table_name
)[
relation
.
primary_key
]
when
String
if
column
.
include?
(
'.'
)
table
,
col
=
column
.
split
(
'.'
)
Arel
::
Table
.
new
(
table
)[
col
]
else
Arel
::
Table
.
new
(
relation
.
table_name
)[
column
]
end
when
Symbol
arelize_column
(
relation
,
column
.
to_s
)
end
end
def
parse_source
(
relation
,
column
)
column
.
relation
.
name
||
relation
.
table_name
end
def
collector
(
connection
)
Arel
::
Collectors
::
SubstituteBinds
.
new
(
connection
,
Arel
::
Collectors
::
SQLString
.
new
)
end
def
arel_query
(
relation
:,
column:
nil
,
distinct:
nil
)
column
||=
relation
.
primary_key
if
column
.
is_a?
(
Arel
::
Attribute
)
relation
.
select
(
column
.
count
(
distinct
)).
arel
else
relation
.
select
(
relation
.
all
.
table
[
column
].
count
(
distinct
)).
arel
end
end
end
end
end
end
end
lib/gitlab/usage/metrics/names_suggestions/generator.rb
View file @
b966a20f
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment