From 30bcc62b6b964a6b6ae3782d120448fac7ffbd70 Mon Sep 17 00:00:00 2001 From: akartasov Date: Tue, 9 Aug 2022 17:19:18 +0700 Subject: [PATCH 1/8] feat(engine): manage state of not configured retrieval (#408) --- cloudformation/dle_cf_template.yaml | 3 + engine/internal/retrieval/retrieval.go | 55 ++++++++++++++++ engine/internal/retrieval/retrieval_test.go | 70 +++++++++++++++++++++ engine/internal/srv/config.go | 7 +++ engine/pkg/models/retrieval.go | 2 + 5 files changed, 137 insertions(+) diff --git a/cloudformation/dle_cf_template.yaml b/cloudformation/dle_cf_template.yaml index b5acd3e36..8cedb3aa0 100644 --- a/cloudformation/dle_cf_template.yaml +++ b/cloudformation/dle_cf_template.yaml @@ -404,6 +404,9 @@ Resources: dle_config_path="/home/ubuntu/.dblab/engine/configs" dle_meta_path="/home/ubuntu/.dblab/engine/meta" postgres_conf_path="/home/ubuntu/.dblab/postgres_conf" + + # Create a special marker file to identify that the DLE is running as a managed instance and has not yet been configured. + touch $dle_meta_path/pending.retrieval yq e -i ' .global.debug=${DLEDebugMode} | diff --git a/engine/internal/retrieval/retrieval.go b/engine/internal/retrieval/retrieval.go index 4229bf2a6..ee3e14c2b 100644 --- a/engine/internal/retrieval/retrieval.go +++ b/engine/internal/retrieval/retrieval.go @@ -8,6 +8,7 @@ package retrieval import ( "context" "fmt" + "os" "strings" "time" @@ -27,6 +28,7 @@ import ( "gitlab.com/postgres-ai/database-lab/v3/internal/retrieval/engine/postgres/snapshot" "gitlab.com/postgres-ai/database-lab/v3/internal/retrieval/engine/postgres/tools/cont" "gitlab.com/postgres-ai/database-lab/v3/internal/telemetry" + "gitlab.com/postgres-ai/database-lab/v3/pkg/util" dblabCfg "gitlab.com/postgres-ai/database-lab/v3/pkg/config" "gitlab.com/postgres-ai/database-lab/v3/pkg/config/global" @@ -38,6 +40,8 @@ const ( parseOption = cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow refreshJobs jobGroup = "refresh" snapshotJobs jobGroup = "snapshot" + + pendingFilename = "pending.retrieval" ) type jobGroup string @@ -86,9 +90,48 @@ func New(cfg *dblabCfg.Config, engineProps global.EngineProps, docker *client.Cl r.setup(retrievalCfg) + if err := checkPendingMarker(r); err != nil { + return nil, fmt.Errorf("failed to check pending marker: %w", err) + } + return r, nil } +func checkPendingMarker(r *Retrieval) error { + pendingPath, err := util.GetMetaPath(pendingFilename) + if err != nil { + return fmt.Errorf("failed to build pending filename: %w", err) + } + + if _, err := os.Stat(pendingPath); err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } + + return fmt.Errorf("failed to get the pending file info: %w", err) + } + + r.State.Status = models.Pending + + return nil +} + +// RemovePendingMarker removes the file from the metadata directory which specifies that retrieval is pending. +func (r *Retrieval) RemovePendingMarker() error { + pending, err := util.GetMetaPath(pendingFilename) + if err != nil { + return fmt.Errorf("failed to build pending filename: %w", err) + } + + if err := os.Remove(pending); err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + + r.State.Status = models.Inactive + + return nil +} + // Reload reloads retrieval configuration. func (r *Retrieval) Reload(ctx context.Context, retrievalCfg *config.Config) { r.setup(retrievalCfg) @@ -150,6 +193,12 @@ func (r *Retrieval) Run(ctx context.Context) error { log.Msg("Pool to perform data retrieving: ", fsManager.Pool().Name) + if r.State.Status == models.Pending { + log.Msg("Data retrieving suspended due to the Retrieval state is pending") + + return nil + } + if err := r.run(runCtx, fsManager); err != nil { alert := telemetry.Alert{Level: models.RefreshFailed, Message: fmt.Sprintf("Failed to perform initial data retrieving: %s", r.State.Mode)} @@ -437,6 +486,12 @@ func (r *Retrieval) FullRefresh(ctx context.Context) error { return nil } + if r.State.Status == models.Pending { + log.Msg("Data retrieving suspended due to the Retrieval state is pending") + + return nil + } + // Stop previous runs and snapshot schedulers. if r.ctxCancel != nil { r.ctxCancel() diff --git a/engine/internal/retrieval/retrieval_test.go b/engine/internal/retrieval/retrieval_test.go index 99b70a6e3..6e71a06c0 100644 --- a/engine/internal/retrieval/retrieval_test.go +++ b/engine/internal/retrieval/retrieval_test.go @@ -1,9 +1,15 @@ package retrieval import ( + "os" + "path" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "gitlab.com/postgres-ai/database-lab/v3/pkg/models" + "gitlab.com/postgres-ai/database-lab/v3/pkg/util" ) func TestJobGroup(t *testing.T) { @@ -41,3 +47,67 @@ func TestJobGroup(t *testing.T) { assert.Equal(t, tc.group, getJobGroup(tc.jobName)) } } + +func TestPendingMarker(t *testing.T) { + t.Run("check if the marker file affects the retrieval state", func(t *testing.T) { + pendingFilepath, err := util.GetMetaPath(pendingFilename) + require.Nil(t, err) + + tmpDir := path.Dir(pendingFilepath) + + err = os.MkdirAll(tmpDir, 0755) + require.Nil(t, err) + + defer func() { + err := os.RemoveAll(tmpDir) + require.Nil(t, err) + }() + + _, err = os.Create(pendingFilepath) + require.Nil(t, err) + + defer func() { + err := os.Remove(pendingFilepath) + require.Nil(t, err) + }() + + r := &Retrieval{} + + err = checkPendingMarker(r) + require.Nil(t, err) + assert.Equal(t, models.Pending, r.State.Status) + }) + + t.Run("check the deletion of the pending marker", func(t *testing.T) { + pendingFilepath, err := util.GetMetaPath(pendingFilename) + require.Nil(t, err) + + tmpDir := path.Dir(pendingFilepath) + + err = os.MkdirAll(tmpDir, 0755) + require.Nil(t, err) + + defer func() { + err := os.RemoveAll(tmpDir) + require.Nil(t, err) + }() + + _, err = os.Create(pendingFilepath) + require.Nil(t, err) + + defer func() { + err := os.Remove(pendingFilepath) + require.ErrorIs(t, err, os.ErrNotExist) + }() + + r := &Retrieval{ + State: State{ + Status: models.Pending, + }, + } + + err = r.RemovePendingMarker() + require.Nil(t, err) + assert.Equal(t, models.Inactive, r.State.Status) + }) +} diff --git a/engine/internal/srv/config.go b/engine/internal/srv/config.go index 607ffa720..efa4a321a 100644 --- a/engine/internal/srv/config.go +++ b/engine/internal/srv/config.go @@ -66,6 +66,13 @@ func (s *Server) setAdminConfig(w http.ResponseWriter, r *http.Request) { return } + // TODO (akartasov): after fixing notices for https://gitlab.com/postgres-ai/database-lab/-/issues/359 + // Make sure that FullRefresh starts after removing a pending marker. + if err := s.Retrieval.RemovePendingMarker(); err != nil { + api.SendError(w, r, err) + return + } + if err := api.WriteJSON(w, http.StatusOK, applied); err != nil { api.SendError(w, r, err) return diff --git a/engine/pkg/models/retrieval.go b/engine/pkg/models/retrieval.go index f4e8cbe6d..eddb5c01c 100644 --- a/engine/pkg/models/retrieval.go +++ b/engine/pkg/models/retrieval.go @@ -26,6 +26,8 @@ type RetrievalStatus string const ( // Inactive defines status when data retrieving is disabled. Inactive RetrievalStatus = "inactive" + // Pending defines status when data retrieving is pending configuration verification. + Pending RetrievalStatus = "pending" // Failed defines status when data retrieving is failed. Failed RetrievalStatus = "failed" // Refreshing defines status when data retrieving is in progress. -- GitLab From aa00c45eebd7234473eb9307681853b1fb599c29 Mon Sep 17 00:00:00 2001 From: akartasov Date: Wed, 10 Aug 2022 07:51:31 +0700 Subject: [PATCH 2/8] fix: change the retrieval status after removing the pending marker just once --- engine/internal/retrieval/retrieval.go | 6 +++++- engine/internal/retrieval/retrieval_test.go | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/engine/internal/retrieval/retrieval.go b/engine/internal/retrieval/retrieval.go index ee3e14c2b..dc4f8f875 100644 --- a/engine/internal/retrieval/retrieval.go +++ b/engine/internal/retrieval/retrieval.go @@ -123,7 +123,11 @@ func (r *Retrieval) RemovePendingMarker() error { return fmt.Errorf("failed to build pending filename: %w", err) } - if err := os.Remove(pending); err != nil && !errors.Is(err, os.ErrNotExist) { + if err := os.Remove(pending); err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } + return err } diff --git a/engine/internal/retrieval/retrieval_test.go b/engine/internal/retrieval/retrieval_test.go index 6e71a06c0..924610804 100644 --- a/engine/internal/retrieval/retrieval_test.go +++ b/engine/internal/retrieval/retrieval_test.go @@ -109,5 +109,11 @@ func TestPendingMarker(t *testing.T) { err = r.RemovePendingMarker() require.Nil(t, err) assert.Equal(t, models.Inactive, r.State.Status) + + r.State.Status = models.Finished + + err = r.RemovePendingMarker() + require.Nil(t, err) + assert.Equal(t, models.Finished, r.State.Status) }) } -- GitLab From 4b9168a2cb2f6c3f99999a6c1eda03d7eb002591 Mon Sep 17 00:00:00 2001 From: Adrinlol Date: Fri, 12 Aug 2022 12:22:10 +0400 Subject: [PATCH 3/8] Add new status type to instance state (#408) --- ui/packages/shared/types/api/entities/instanceState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/packages/shared/types/api/entities/instanceState.ts b/ui/packages/shared/types/api/entities/instanceState.ts index f882206c5..2f56f76e9 100644 --- a/ui/packages/shared/types/api/entities/instanceState.ts +++ b/ui/packages/shared/types/api/entities/instanceState.ts @@ -27,7 +27,7 @@ export type InstanceStateDto = { lastRefresh: string | null nextRefresh: string | null mode: string - status: 'finished' | 'failed' | 'refreshing' + status: 'finished' | 'failed' | 'refreshing' | 'pending' alerts?: { refresh_failed?: { level: 'error' -- GitLab From 5a4b606a963aaebff9f5ba86a0a0c5d6dc74106e Mon Sep 17 00:00:00 2001 From: Adrinlol Date: Fri, 12 Aug 2022 12:23:57 +0400 Subject: [PATCH 4/8] Set initial active tab based on retrieving status (#408) --- ui/packages/shared/pages/Instance/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/packages/shared/pages/Instance/index.tsx b/ui/packages/shared/pages/Instance/index.tsx index 38936a149..49e63cc4b 100644 --- a/ui/packages/shared/pages/Instance/index.tsx +++ b/ui/packages/shared/pages/Instance/index.tsx @@ -64,6 +64,7 @@ export const Instance = observer((props: Props) => { const { instanceId, api } = props const stores = useCreatedStores(props) + const retrievingPending = stores.main.instance?.state.retrieving?.status === "pending"; useEffect(() => { stores.main.load(instanceId) @@ -80,7 +81,7 @@ export const Instance = observer((props: Props) => { } }, [instance]) - const [activeTab, setActiveTab] = React.useState(0); + const [activeTab, setActiveTab] = React.useState(retrievingPending ? 2 : 0); const [isLogConnectionEnabled, enableLogConnection] = React.useState(false); -- GitLab From 114dff3d79f863745a36ebbd4d1ba8ab3495bfc7 Mon Sep 17 00:00:00 2001 From: Nikolay Samokhvalov Date: Mon, 15 Aug 2022 02:58:09 +0000 Subject: [PATCH 5/8] Apply 1 suggestion(s) to 1 file(s) --- engine/internal/retrieval/retrieval.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/internal/retrieval/retrieval.go b/engine/internal/retrieval/retrieval.go index dc4f8f875..391dcb386 100644 --- a/engine/internal/retrieval/retrieval.go +++ b/engine/internal/retrieval/retrieval.go @@ -108,7 +108,7 @@ func checkPendingMarker(r *Retrieval) error { return nil } - return fmt.Errorf("failed to get the pending file info: %w", err) + return fmt.Errorf("failed to get pending file info: %w", err) } r.State.Status = models.Pending -- GitLab From 988f6a3f1c41ae22ad22c578c0f52abe24a933e5 Mon Sep 17 00:00:00 2001 From: Nikolay Samokhvalov Date: Mon, 15 Aug 2022 02:58:12 +0000 Subject: [PATCH 6/8] Apply 1 suggestion(s) to 1 file(s) --- engine/internal/retrieval/retrieval.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/internal/retrieval/retrieval.go b/engine/internal/retrieval/retrieval.go index 391dcb386..651302116 100644 --- a/engine/internal/retrieval/retrieval.go +++ b/engine/internal/retrieval/retrieval.go @@ -198,7 +198,7 @@ func (r *Retrieval) Run(ctx context.Context) error { log.Msg("Pool to perform data retrieving: ", fsManager.Pool().Name) if r.State.Status == models.Pending { - log.Msg("Data retrieving suspended due to the Retrieval state is pending") + log.Msg("Data retrieving suspended because Retrieval state is pending") return nil } -- GitLab From 691a1518f6aa11cd26477e6bb9c32725d9e78414 Mon Sep 17 00:00:00 2001 From: Nikolay Samokhvalov Date: Mon, 15 Aug 2022 02:58:18 +0000 Subject: [PATCH 7/8] Apply 1 suggestion(s) to 1 file(s) --- engine/internal/retrieval/retrieval.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/internal/retrieval/retrieval.go b/engine/internal/retrieval/retrieval.go index 651302116..0086a384b 100644 --- a/engine/internal/retrieval/retrieval.go +++ b/engine/internal/retrieval/retrieval.go @@ -491,7 +491,7 @@ func (r *Retrieval) FullRefresh(ctx context.Context) error { } if r.State.Status == models.Pending { - log.Msg("Data retrieving suspended due to the Retrieval state is pending") + log.Msg("Data retrieving suspended because Retrieval state is pending") return nil } -- GitLab From 406e1476b1cc3eab532f3cae3056a902b1d83891 Mon Sep 17 00:00:00 2001 From: Nikolay Samokhvalov Date: Mon, 15 Aug 2022 10:02:32 +0000 Subject: [PATCH 8/8] Apply 1 suggestion(s) to 1 file(s) --- cloudformation/dle_cf_template.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cloudformation/dle_cf_template.yaml b/cloudformation/dle_cf_template.yaml index 8cedb3aa0..bef47c347 100644 --- a/cloudformation/dle_cf_template.yaml +++ b/cloudformation/dle_cf_template.yaml @@ -405,7 +405,8 @@ Resources: dle_meta_path="/home/ubuntu/.dblab/engine/meta" postgres_conf_path="/home/ubuntu/.dblab/postgres_conf" - # Create a special marker file to identify that the DLE is running as a managed instance and has not yet been configured. + # Create a special marker file to identify that the DLE is running as a "managed" instance + # (e.g., launched using Marketplace or Terraform), and has not yet been configured. touch $dle_meta_path/pending.retrieval yq e -i ' -- GitLab