[WEB-2706] fix: Add fallback when db initialisation fails (#5973)
* Add fallback when db initialization fails * add checks for instance.exec * chore: convert issue boolean fields to actual boolean value. * change instance exec code * sync issue to local db when inbox issue is accepted and draft issue is moved to project * chore: added project and workspace keys --------- Co-authored-by: rahulramesha <rahulramesham@gmail.com> Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
This commit is contained in:
parent
2193e8c79c
commit
acba451803
8 changed files with 78 additions and 39 deletions
|
|
@ -95,6 +95,8 @@ class IssueCreateSerializer(BaseSerializer):
|
|||
write_only=True,
|
||||
required=False,
|
||||
)
|
||||
project_id = serializers.UUIDField(source="project.id", read_only=True)
|
||||
workspace_id = serializers.UUIDField(source="workspace.id", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Issue
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { rootStore } from "@/lib/store-context";
|
|||
// services
|
||||
import { IssueService } from "@/services/issue/issue.service";
|
||||
//
|
||||
import { ARRAY_FIELDS } from "./utils/constants";
|
||||
import { ARRAY_FIELDS, BOOLEAN_FIELDS } from "./utils/constants";
|
||||
import { getSubIssuesWithDistribution } from "./utils/data.utils";
|
||||
import createIndexes from "./utils/indexes";
|
||||
import { addIssuesBulk, syncDeletesToLocal } from "./utils/load-issues";
|
||||
|
|
@ -75,6 +75,7 @@ export class Storage {
|
|||
if (workspaceSlug !== this.workspaceSlug) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
try {
|
||||
await startSpan({ name: "INIT_DB" }, async () => await this._initialize(workspaceSlug));
|
||||
return true;
|
||||
|
|
@ -125,6 +126,7 @@ export class Storage {
|
|||
return true;
|
||||
} catch (error) {
|
||||
this.status = "error";
|
||||
this.db = null;
|
||||
throw new Error(`Failed to initialize database worker: ${error}`);
|
||||
}
|
||||
};
|
||||
|
|
@ -467,5 +469,9 @@ export const formatLocalIssue = (issue: any) => {
|
|||
ARRAY_FIELDS.forEach((field: string) => {
|
||||
currIssue[field] = currIssue[field] ? JSON.parse(currIssue[field]) : [];
|
||||
});
|
||||
// Convert boolean fields to actual boolean values
|
||||
BOOLEAN_FIELDS.forEach((field: string) => {
|
||||
currIssue[field] = currIssue[field] === 1;
|
||||
});
|
||||
return currIssue as TIssue & { group_id?: string; total_issues: number; sub_group_id?: string };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
export const ARRAY_FIELDS = ["label_ids", "assignee_ids", "module_ids"];
|
||||
|
||||
export const BOOLEAN_FIELDS = ["is_draft"];
|
||||
|
||||
export const GROUP_BY_MAP = {
|
||||
state_id: "state_id",
|
||||
priority: "priority",
|
||||
|
|
|
|||
|
|
@ -19,17 +19,9 @@ export const logError = (e: any) => {
|
|||
};
|
||||
export const logInfo = console.info;
|
||||
|
||||
export const updatePersistentLayer = async (issueIds: string | string[]) => {
|
||||
if (typeof issueIds === "string") {
|
||||
issueIds = [issueIds];
|
||||
}
|
||||
issueIds.forEach(async (issueId) => {
|
||||
const dbIssue = await persistence.getIssue(issueId);
|
||||
const issue = rootStore.issue.issues.getIssueById(issueId);
|
||||
|
||||
if (issue) {
|
||||
// JSON.parse(JSON.stringify(issue)) is used to remove the mobx observables
|
||||
const issuePartial = pick({ ...dbIssue, ...JSON.parse(JSON.stringify(issue)) }, [
|
||||
export const addIssueToPersistanceLayer = async (issue: TIssue) => {
|
||||
try {
|
||||
const issuePartial = pick({ ...JSON.parse(JSON.stringify(issue)) }, [
|
||||
"id",
|
||||
"name",
|
||||
"state_id",
|
||||
|
|
@ -60,6 +52,21 @@ export const updatePersistentLayer = async (issueIds: string | string[]) => {
|
|||
"description_html",
|
||||
]);
|
||||
await updateIssue({ ...issuePartial, is_local_update: 1 });
|
||||
} catch (e) {
|
||||
logError("Error while adding issue to db");
|
||||
}
|
||||
};
|
||||
|
||||
export const updatePersistentLayer = async (issueIds: string | string[]) => {
|
||||
if (typeof issueIds === "string") {
|
||||
issueIds = [issueIds];
|
||||
}
|
||||
issueIds.forEach(async (issueId) => {
|
||||
const dbIssue = await persistence.getIssue(issueId);
|
||||
const issue = rootStore.issue.issues.getIssueById(issueId);
|
||||
|
||||
if (issue) {
|
||||
addIssueToPersistanceLayer(issue);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,12 +36,20 @@ export class DBClass {
|
|||
this.sqlite3 = SQLite.Factory(m);
|
||||
const vfs = await MyVFS.create("plane", m);
|
||||
this.sqlite3.vfs_register(vfs, true);
|
||||
const db = await this.sqlite3.open_v2(
|
||||
// Fallback in rare cases where the database is not initialized in time
|
||||
const p = new Promise((resolve) => setTimeout(() => resolve(false), 2000));
|
||||
const dbPromise = this.sqlite3.open_v2(
|
||||
`${dbName}.sqlite3`,
|
||||
this.sqlite3.OPEN_READWRITE | this.sqlite3.OPEN_CREATE,
|
||||
"plane"
|
||||
);
|
||||
|
||||
const db = await Promise.any([dbPromise, p]);
|
||||
|
||||
if (!db) {
|
||||
throw new Error("Failed to initialize in time");
|
||||
}
|
||||
|
||||
this.instance.db = db;
|
||||
this.instance.exec = async (sql: string) => {
|
||||
const rows: any[] = [];
|
||||
|
|
@ -58,7 +66,7 @@ export class DBClass {
|
|||
}
|
||||
|
||||
runQuery(sql: string) {
|
||||
return this.instance.exec(sql);
|
||||
return this.instance?.exec?.(sql);
|
||||
}
|
||||
|
||||
async exec(props: string | TQueryProps) {
|
||||
|
|
@ -103,14 +111,14 @@ export class DBClass {
|
|||
}
|
||||
|
||||
if (sql === "COMMIT;" && this.tp) {
|
||||
await this.instance.exec(sql);
|
||||
await this.instance?.exec?.(sql);
|
||||
if (this.tp.length > 0) {
|
||||
const { resolve } = this.tpResolver.shift();
|
||||
resolve();
|
||||
}
|
||||
return;
|
||||
}
|
||||
return await this.instance.exec(sql);
|
||||
return await this.instance?.exec?.(sql);
|
||||
}
|
||||
async close() {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export class WorkspaceDraftService extends APIService {
|
|||
});
|
||||
}
|
||||
|
||||
async moveIssue(workspaceSlug: string, issueId: string, payload: Partial<TWorkspaceDraftIssue>): Promise<void> {
|
||||
async moveIssue(workspaceSlug: string, issueId: string, payload: Partial<TWorkspaceDraftIssue>): Promise<TIssue> {
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/draft-to-issue/${issueId}/`, payload)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import { makeObservable, observable, runInAction, action } from "mobx";
|
|||
import { TIssue, TInboxIssue, TInboxIssueStatus, TInboxDuplicateIssueDetails } from "@plane/types";
|
||||
// helpers
|
||||
import { EInboxIssueStatus } from "@/helpers/inbox.helper";
|
||||
// local db
|
||||
import { addIssueToPersistanceLayer } from "@/local-db/utils/utils";
|
||||
// services
|
||||
import { InboxIssueService } from "@/services/inbox";
|
||||
import { IssueService } from "@/services/issue";
|
||||
|
|
@ -88,10 +90,16 @@ export class InboxIssueStore implements IInboxIssueStore {
|
|||
|
||||
try {
|
||||
if (!this.issue.id) return;
|
||||
|
||||
const inboxIssue = await this.inboxIssueService.update(this.workspaceSlug, this.projectId, this.issue.id, {
|
||||
status: status,
|
||||
});
|
||||
runInAction(() => set(this, "status", inboxIssue?.status));
|
||||
|
||||
// If issue accepted sync issue to local db
|
||||
if (status === EInboxIssueStatus.ACCEPTED) {
|
||||
addIssueToPersistanceLayer(inboxIssue.issue);
|
||||
}
|
||||
} catch {
|
||||
runInAction(() => set(this, "status", previousData.status));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import {
|
|||
import { EDraftIssuePaginationType } from "@/constants/workspace-drafts";
|
||||
// helpers
|
||||
import { getCurrentDateTimeInISO, convertToISODateString } from "@/helpers/date-time.helper";
|
||||
// local-db
|
||||
import { addIssueToPersistanceLayer } from "@/local-db/utils/utils";
|
||||
// services
|
||||
import workspaceDraftService from "@/services/issue/workspace_draft.service";
|
||||
// types
|
||||
|
|
@ -59,7 +61,7 @@ export interface IWorkspaceDraftIssues {
|
|||
payload: Partial<TWorkspaceDraftIssue | TIssue>
|
||||
) => Promise<TWorkspaceDraftIssue | undefined>;
|
||||
deleteIssue: (workspaceSlug: string, issueId: string) => Promise<void>;
|
||||
moveIssue: (workspaceSlug: string, issueId: string, payload: Partial<TWorkspaceDraftIssue>) => Promise<void>;
|
||||
moveIssue: (workspaceSlug: string, issueId: string, payload: Partial<TWorkspaceDraftIssue>) => Promise<TIssue>;
|
||||
addCycleToIssue: (
|
||||
workspaceSlug: string,
|
||||
issueId: string,
|
||||
|
|
@ -348,6 +350,10 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues {
|
|||
total_count: this.paginationInfo.total_count - 1,
|
||||
});
|
||||
}
|
||||
|
||||
// sync issue to local db
|
||||
addIssueToPersistanceLayer(response);
|
||||
|
||||
// Update draft issue count in workspaceUserInfo
|
||||
this.updateWorkspaceUserDraftIssueCount(workspaceSlug, -1);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue