diff --git a/config/metrics/counts_all/20210216180232_projects_jira_dvcs_cloud_active.yml b/config/metrics/counts_all/20210216180232_projects_jira_dvcs_cloud_active.yml
index 29edaa6ab3b4140daa818b7b4730327c52011a88..5bf8e1d6e7820b86da26af027518c9332cb22dca 100644
--- a/config/metrics/counts_all/20210216180232_projects_jira_dvcs_cloud_active.yml
+++ b/config/metrics/counts_all/20210216180232_projects_jira_dvcs_cloud_active.yml
@@ -9,6 +9,9 @@ value_type: number
 status: active
 time_frame: all
 data_source: database
+instrumentation_class: CountProjectsWithJiraDvcsIntegrationMetric
+options:
+  cloud: true
 distribution:
 - ce
 - ee
diff --git a/config/metrics/counts_all/20210216180234_projects_jira_dvcs_server_active.yml b/config/metrics/counts_all/20210216180234_projects_jira_dvcs_server_active.yml
index 9673956c7e14239370647b624bb9811fd2249ee9..bfb402c257ea98b24930c4db4c771ff4c32d8600 100644
--- a/config/metrics/counts_all/20210216180234_projects_jira_dvcs_server_active.yml
+++ b/config/metrics/counts_all/20210216180234_projects_jira_dvcs_server_active.yml
@@ -9,6 +9,9 @@ value_type: number
 status: active
 time_frame: all
 data_source: database
+instrumentation_class: CountProjectsWithJiraDvcsIntegrationMetric
+options:
+  cloud: false
 distribution:
 - ce
 - ee
diff --git a/ee/config/metrics/counts_all/20210216180236_projects_jira_issuelist_active.yml b/ee/config/metrics/counts_all/20210216180236_projects_jira_issuelist_active.yml
index 7384333f8485f520964ade79e95f575b67633d40..bbee0595a21ec9f17f05902beb6a4b605142e547 100644
--- a/ee/config/metrics/counts_all/20210216180236_projects_jira_issuelist_active.yml
+++ b/ee/config/metrics/counts_all/20210216180236_projects_jira_issuelist_active.yml
@@ -9,6 +9,7 @@ value_type: number
 status: active
 time_frame: all
 data_source: database
+instrumentation_class: CountProjectsWithJiraIssuelistActiveMetric
 distribution:
 - ee
 tier:
diff --git a/ee/lib/ee/gitlab/usage_data.rb b/ee/lib/ee/gitlab/usage_data.rb
index aa72901a7195557eb58026108b12879c3ebf1d7f..86b00724094e543a44b266d1f4179e4189b06885 100644
--- a/ee/lib/ee/gitlab/usage_data.rb
+++ b/ee/lib/ee/gitlab/usage_data.rb
@@ -139,13 +139,6 @@ def system_usage_data
           end
         end
 
-        override :jira_usage
-        def jira_usage
-          super.merge(
-            projects_jira_issuelist_active: projects_jira_issuelist_active
-          )
-        end
-
         # Omitted because no user, creator or author associated: `auto_devops_disabled`, `auto_devops_enabled`
         # Omitted because not in use anymore: `gcp_clusters`, `gcp_clusters_disabled`, `gcp_clusters_enabled`
         # rubocop:disable CodeReuse/ActiveRecord
@@ -335,17 +328,6 @@ def merge_requests_with_overridden_project_rules(time_period = nil)
           )
         end
 
-        def projects_jira_issuelist_active
-          # rubocop: disable UsageData/LargeTable:
-          min_id = minimum_id(::Integrations::JiraTrackerData.where(issues_enabled: true), :integration_id)
-          max_id = maximum_id(::Integrations::JiraTrackerData.where(issues_enabled: true), :integration_id)
-          # rubocop: enable UsageData/LargeTable:
-          # rubocop: disable UsageData/DistinctCountByLargeForeignKey
-          distinct_count(::Integrations::Jira.active.left_outer_joins(:jira_tracker_data).where(jira_tracker_data: { issues_enabled: true }), start: min_id, finish: max_id)
-          # rubocop: enable UsageData/DistinctCountByLargeForeignKey
-        end
-        # rubocop:enable CodeReuse/ActiveRecord
-
         # rubocop:disable CodeReuse/ActiveRecord
         def projects_with_sectional_code_owner_rules(time_period)
           distinct_count(
diff --git a/ee/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric.rb b/ee/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric.rb
new file mode 100644
index 0000000000000000000000000000000000000000..df8f1dc1bec3470435a27bade2f7864e4af46548
--- /dev/null
+++ b/ee/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+  module Usage
+    module Metrics
+      module Instrumentations
+        class CountProjectsWithJiraIssuelistActiveMetric < DatabaseMetric
+          operation :distinct_count
+
+          start do
+            ::Integrations::JiraTrackerData.where(issues_enabled: true).minimum(:integration_id)
+          end
+
+          finish do
+            ::Integrations::JiraTrackerData.where(issues_enabled: true).maximum(:integration_id)
+          end
+
+          relation do
+            ::Integrations::Jira
+              .active
+              .left_outer_joins(:jira_tracker_data)
+              .where(jira_tracker_data: { issues_enabled: true })
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/ee/spec/lib/ee/gitlab/usage_data_spec.rb b/ee/spec/lib/ee/gitlab/usage_data_spec.rb
index bfd41f42c5e6270b603e8947c246909346136e0c..4d01f72628012ade77a7ee5af6436d46fd48fe6f 100644
--- a/ee/spec/lib/ee/gitlab/usage_data_spec.rb
+++ b/ee/spec/lib/ee/gitlab/usage_data_spec.rb
@@ -96,7 +96,6 @@
           ldap_users
           operations_dashboard_default_dashboard
           operations_dashboard_users_with_projects_added
-          projects_jira_issuelist_active
           projects_mirrored_with_pipelines_enabled
           projects_reporting_ci_cd_back_to_github
           status_page_issues
@@ -108,7 +107,6 @@
       expect(count_data[:status_page_projects]).to eq(1)
       expect(count_data[:status_page_issues]).to eq(1)
       expect(count_data[:issues_with_health_status]).to eq(2)
-      expect(count_data[:projects_jira_issuelist_active]).to eq(1)
       expect(count_data[:epic_issues]).to eq(2)
     end
 
diff --git a/ee/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric_spec.rb b/ee/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e0420c91012c92c5a9e334b5204f382734875bab
--- /dev/null
+++ b/ee/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountProjectsWithJiraIssuelistActiveMetric,
+  feature_category: :integrations do
+  let_it_be(:project_1) { create(:project) }
+  let_it_be(:project_2) { create(:project) }
+  let_it_be(:jira_project_with_issuelist) do
+    create(:jira_integration, project: project_1, issues_enabled: true, project_key: 'foo')
+  end
+
+  let_it_be(:jira_project_without_issuelist) do
+    create(:jira_integration, project: project_2, issues_enabled: false, project_key: 'bar')
+  end
+
+  let(:expected_value) { 1 }
+  let(:expected_query) do
+    'SELECT COUNT(DISTINCT "integrations"."id") FROM "integrations" ' \
+      'LEFT OUTER JOIN "jira_tracker_data" ON "jira_tracker_data"."integration_id" = "integrations"."id" ' \
+      'WHERE "integrations"."type_new" = \'Integrations::Jira\' AND "integrations"."active" = TRUE ' \
+      'AND "jira_tracker_data"."issues_enabled" = TRUE'
+  end
+
+  it_behaves_like 'a correct instrumented metric value and query', { time_frame: 'all', data_source: 'database' }
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric.rb b/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric.rb
new file mode 100644
index 0000000000000000000000000000000000000000..25a45a259e2876a5817643271277bb2798e36527
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+  module Usage
+    module Metrics
+      module Instrumentations
+        class CountProjectsWithJiraDvcsIntegrationMetric < DatabaseMetric
+          operation :count
+
+          def initialize(metric_definition)
+            super
+
+            raise ArgumentError, "option 'cloud' must be a boolean" unless [true, false].include?(options[:cloud])
+          end
+
+          relation do |options|
+            ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: options[:cloud])
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 72168bce782f8804fb6842261ecf7031f10ce479..2da16cf8725156bb28e3d5982262ae7cf685bb99 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -286,16 +286,9 @@ def integrations_usage
           response[:"instances_#{name}_active"] = count(Integration.active.where(instance: true, type: type))
           response[:"projects_inheriting_#{name}_active"] = count(Integration.active.where.not(project: nil).where.not(inherit_from_id: nil).where(type: type))
           response[:"groups_inheriting_#{name}_active"] = count(Integration.active.where.not(group: nil).where.not(inherit_from_id: nil).where(type: type))
-        end.merge(jira_usage, jira_import_usage)
+        end.merge(jira_import_usage)
         # rubocop: enable UsageData/LargeTable:
       end
-
-      def jira_usage
-        {
-          projects_jira_dvcs_cloud_active: count(ProjectFeatureUsage.with_jira_dvcs_integration_enabled),
-          projects_jira_dvcs_server_active: count(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false))
-        }
-      end
       # rubocop: enable CodeReuse/ActiveRecord
 
       def jira_import_usage
diff --git a/rubocop/rubocop-code_reuse.yml b/rubocop/rubocop-code_reuse.yml
index c1babff4b12ead898fd985e47f9f80a7c5e4cc4d..f96de5caf994d1e8a7880d8c3c2dc46f148cf8bb 100644
--- a/rubocop/rubocop-code_reuse.yml
+++ b/rubocop/rubocop-code_reuse.yml
@@ -42,3 +42,4 @@ CodeReuse/ActiveRecord:
     - ee/lib/tasks/**/*.rake
     - ee/lib/ee/gitlab/background_migration/**/*.rb
     - ee/lib/gitlab/llm/open_ai/response_modifiers/tanuki_bot.rb
+    - ee/lib/gitlab/usage/metrics/instrumentations/**/*.rb
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a2d86fc50444202ae7d9cd844af0a96bd1c1a6d5
--- /dev/null
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountProjectsWithJiraDvcsIntegrationMetric,
+  feature_category: :integrations do
+  describe 'metric value and query' do
+    let_it_be_with_reload(:project_1) { create(:project) }
+    let_it_be_with_reload(:project_2) { create(:project) }
+    let_it_be_with_reload(:project_3) { create(:project) }
+
+    before do
+      project_1.feature_usage.log_jira_dvcs_integration_usage(cloud: false)
+      project_2.feature_usage.log_jira_dvcs_integration_usage(cloud: false)
+      project_3.feature_usage.log_jira_dvcs_integration_usage(cloud: true)
+    end
+
+    context 'when counting cloud integrations' do
+      let(:expected_value) { 1 }
+      let(:expected_query) do
+        'SELECT COUNT("project_feature_usages"."project_id") FROM "project_feature_usages" ' \
+          'WHERE "project_feature_usages"."jira_dvcs_cloud_last_sync_at" IS NOT NULL'
+      end
+
+      it_behaves_like 'a correct instrumented metric value and query', { time_frame: 'all', options: { cloud: true } }
+    end
+
+    context 'when counting non-cloud integrations' do
+      let(:expected_value) { 2 }
+      let(:expected_query) do
+        'SELECT COUNT("project_feature_usages"."project_id") FROM "project_feature_usages" ' \
+          'WHERE "project_feature_usages"."jira_dvcs_server_last_sync_at" IS NOT NULL'
+      end
+
+      it_behaves_like 'a correct instrumented metric value and query', { time_frame: 'all', options: { cloud: false } }
+    end
+  end
+
+  it "raises an exception if option is not present" do
+    expect do
+      described_class.new(options: {}, time_frame: 'all')
+    end.to raise_error(ArgumentError, %r{must be a boolean})
+  end
+
+  it "raises an exception if option has invalid value" do
+    expect do
+      described_class.new(options: { cloud: 'yes' }, time_frame: 'all')
+    end.to raise_error(ArgumentError, %r{must be a boolean})
+  end
+end
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 73f7a79dd5be5aa93055a6006df5b0b75b9f65f9..8ac3b0c134b819066269272f989ef57fa581af7a 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -50,8 +50,6 @@ module UsageDataHelpers
     projects_asana_active
     projects_jenkins_active
     projects_jira_active
-    projects_jira_dvcs_cloud_active
-    projects_jira_dvcs_server_active
     projects_slack_active
     projects_slack_slash_commands_active
     projects_custom_issue_tracker_active