summaryrefslogtreecommitdiff
path: root/doc/development/geo/framework.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/geo/framework.md')
-rw-r--r--doc/development/geo/framework.md261
1 files changed, 260 insertions, 1 deletions
diff --git a/doc/development/geo/framework.md b/doc/development/geo/framework.md
index e4518ce1b57..148953dc418 100644
--- a/doc/development/geo/framework.md
+++ b/doc/development/geo/framework.md
@@ -287,7 +287,7 @@ For example, to add support for files referenced by a `Widget` model with a
t.datetime_with_timezone :created_at, null: false
t.text :last_sync_failure
- t.index :widget_id, name: :index_widget_registry_on_widget_id
+ t.index :widget_id, name: :index_widget_registry_on_widget_id, unique: true
t.index :retry_at
t.index :state
end
@@ -743,6 +743,8 @@ available in the Admin UI.
#### Releasing the feature
+1. In `ee/config/feature_flags/development/geo_widget_replication.yml`, set `default_enabled: true`
+
1. In `ee/app/replicators/geo/widget_replicator.rb`, delete the `self.replication_enabled_by_default?` method:
```ruby
@@ -770,3 +772,260 @@ available in the Admin UI.
description: 'Find widget registries on this Geo node',
feature_flag: :geo_widget_replication # REMOVE THIS LINE
```
+
+### Repository Replicator Strategy
+
+Models that refer to any repository on the disk
+can be easily supported by Geo with the `Geo::RepositoryReplicatorStrategy` module.
+
+For example, to add support for files referenced by a `Gizmos` model with a
+`gizmos` table, you would perform the following steps.
+
+#### Replication
+
+1. Include `Gitlab::Geo::ReplicableModel` in the `Gizmo` class, and specify
+ the Replicator class `with_replicator Geo::GizmoReplicator`.
+
+ At this point the `Gizmo` class should look like this:
+
+ ```ruby
+ # frozen_string_literal: true
+
+ class Gizmo < ApplicationRecord
+ include ::Gitlab::Geo::ReplicableModel
+
+ with_replicator Geo::GizmoReplicator
+
+ # @param primary_key_in [Range, Gizmo] arg to pass to primary_key_in scope
+ # @return [ActiveRecord::Relation<Gizmo>] everything that should be synced to this node, restricted by primary key
+ def self.replicables_for_current_secondary(primary_key_in)
+ # Should be implemented. The idea of the method is to restrict
+ # the set of synced items depending on synchronization settings
+ end
+
+ # Geo checks this method in FrameworkRepositorySyncService to avoid
+ # snapshotting repositories using object pools
+ def pool_repository
+ nil
+ end
+ ...
+ end
+ ```
+
+ Pay some attention to method `pool_repository`. Not every repository type uses
+ repository pooling. As Geo prefers to use repository snapshotting, it can lead to data loss.
+ Make sure to overwrite `pool_repository` so it returns nil for repositories that do not
+ have pools.
+
+ If there is a common constraint for records to be available for replication,
+ make sure to also overwrite the `available_replicables` scope.
+
+1. Create `ee/app/replicators/geo/gizmo_replicator.rb`. Implement the
+ `#repository` method which should return a `<Repository>` instance,
+ and implement the class method `.model` to return the `Gizmo` class:
+
+ ```ruby
+ # frozen_string_literal: true
+
+ module Geo
+ class GizmoReplicator < Gitlab::Geo::Replicator
+ include ::Geo::RepositoryReplicatorStrategy
+
+ def self.model
+ ::Gizmo
+ end
+
+ def repository
+ model_record.repository
+ end
+
+ def self.git_access_class
+ ::Gitlab::GitAccessGizmo
+ end
+
+ # The feature flag follows the format `geo_#{replicable_name}_replication`,
+ # so here it would be `geo_gizmo_replication`
+ def self.replication_enabled_by_default?
+ false
+ end
+ end
+ end
+ ```
+
+1. Generate the feature flag definition file by running the feature flag command
+ and running through the steps:
+
+ ```shell
+ bin/feature-flag --ee geo_gizmo_replication --type development --group 'group::geo'
+ ```
+
+1. Make sure Geo push events are created. Usually it needs some
+ change in the `app/workers/post_receive.rb` file. Example:
+
+ ```ruby
+ def replicate_gizmo_changes(gizmo)
+ if ::Gitlab::Geo.primary?
+ gizmo.replicator.handle_after_update if gizmo
+ end
+ end
+ ```
+
+ See `app/workers/post_receive.rb` for more examples.
+
+1. Make sure the repository removal is also handled. You may need to add something
+ like the following in the destroy service of the repository:
+
+ ```ruby
+ gizmo.replicator.handle_after_destroy if gizmo.repository
+ ```
+
+1. Add this replicator class to the method `replicator_classes` in
+ `ee/lib/gitlab/geo.rb`:
+
+ ```ruby
+ REPLICATOR_CLASSES = [
+ ...
+ ::Geo::PackageFileReplicator,
+ ::Geo::GizmoReplicator
+ ]
+ end
+ ```
+
+1. Create `ee/spec/replicators/geo/gizmo_replicator_spec.rb` and perform
+ the necessary setup to define the `model_record` variable for the shared
+ examples:
+
+ ```ruby
+ # frozen_string_literal: true
+
+ require 'spec_helper'
+
+ RSpec.describe Geo::GizmoReplicator do
+ let(:model_record) { build(:gizmo) }
+
+ include_examples 'a repository replicator'
+ end
+ ```
+
+1. Create the `gizmo_registry` table, with columns ordered according to [our guidelines](../ordering_table_columns.md) so Geo secondaries can track the sync and
+ verification state of each Gizmo. This migration belongs in `ee/db/geo/migrate`:
+
+ ```ruby
+ # frozen_string_literal: true
+
+ class CreateGizmoRegistry < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ create_table :gizmo_registry, id: :bigserial, force: :cascade do |t|
+ t.datetime_with_timezone :retry_at
+ t.datetime_with_timezone :last_synced_at
+ t.datetime_with_timezone :created_at, null: false
+ t.bigint :gizmo_id, null: false
+ t.integer :state, default: 0, null: false, limit: 2
+ t.integer :retry_count, default: 0, limit: 2
+ t.text :last_sync_failure
+ t.boolean :force_to_redownload
+ t.boolean :missing_on_primary
+
+ t.index :gizmo_id, name: :index_gizmo_registry_on_gizmo_id, unique: true
+ t.index :retry_at
+ t.index :state
+ end
+
+ add_text_limit :gizmo_registry, :last_sync_failure, 255
+ end
+
+ def down
+ drop_table :gizmo_registry
+ end
+ end
+ ```
+
+1. Create `ee/app/models/geo/gizmo_registry.rb`:
+
+ ```ruby
+ # frozen_string_literal: true
+
+ class Geo::GizmoRegistry < Geo::BaseRegistry
+ include Geo::ReplicableRegistry
+
+ MODEL_CLASS = ::Gizmo
+ MODEL_FOREIGN_KEY = :gizmo_id
+
+ belongs_to :gizmo, class_name: 'Gizmo'
+ end
+ ```
+
+1. Update `REGISTRY_CLASSES` in `ee/app/workers/geo/secondary/registry_consistency_worker.rb`.
+1. Add `gizmo_registry` to `ActiveSupport::Inflector.inflections` in `config/initializers_before_autoloader/000_inflections.rb`.
+1. Create `ee/spec/factories/geo/gizmo_registry.rb`:
+
+ ```ruby
+ # frozen_string_literal: true
+
+ FactoryBot.define do
+ factory :geo_gizmo_registry, class: 'Geo::GizmoRegistry' do
+ gizmo
+ state { Geo::GizmoRegistry.state_value(:pending) }
+
+ trait :synced do
+ state { Geo::GizmoRegistry.state_value(:synced) }
+ last_synced_at { 5.days.ago }
+ end
+
+ trait :failed do
+ state { Geo::GizmoRegistry.state_value(:failed) }
+ last_synced_at { 1.day.ago }
+ retry_count { 2 }
+ last_sync_failure { 'Random error' }
+ end
+
+ trait :started do
+ state { Geo::GizmoRegistry.state_value(:started) }
+ last_synced_at { 1.day.ago }
+ retry_count { 0 }
+ end
+ end
+ end
+ ```
+
+1. Create `ee/spec/models/geo/gizmo_registry_spec.rb`:
+
+ ```ruby
+ # frozen_string_literal: true
+
+ require 'spec_helper'
+
+ RSpec.describe Geo::GizmoRegistry, :geo, type: :model do
+ let_it_be(:registry) { create(:geo_gizmo_registry) }
+
+ specify 'factory is valid' do
+ expect(registry).to be_valid
+ end
+
+ include_examples 'a Geo framework registry'
+ end
+ ```
+
+1. Make sure the newly added repository type can be accessed by a secondary.
+ You may need to make some changes to one of the Git access classes.
+
+ Gizmos should now be replicated by Geo.
+
+#### Metrics
+
+You need to make the same changes as for Blob Replicator Strategy.
+You need to make the same changes for the [metrics as in the Blob Replicator Strategy](#metrics).
+
+#### GraphQL API
+
+You need to make the same changes for the GraphQL API [as in the Blob Replicator Strategy](#graphql-api).
+
+#### Releasing the feature
+
+You need to make the same changes for [releasing the feature as in the Blob Replicator Strategy](#releasing-the-feature).