Skip to content

Commit

Permalink
feat(server): new projects are placed in a configurable region (#3801)
Browse files Browse the repository at this point in the history
* feat(server): log subscription started messages with info

* feat(server): create projects in a default region

* feat(server): allow project default region config

* feat(server): load project region from multi region config
  • Loading branch information
gjedlicska authored Jan 13, 2025
1 parent 4483531 commit db8de11
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 8 deletions.
31 changes: 28 additions & 3 deletions packages/server/modules/core/graph/resolvers/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ import {
insertCommitsFactory,
insertStreamCommitsFactory
} from '@/modules/core/repositories/commits'
import { storeModelFactory } from '@/modules/core/repositories/models'
import {
deleteProjectFactory,
getProjectFactory,
storeProjectFactory,
storeProjectRoleFactory
} from '@/modules/core/repositories/projects'
import { getServerInfoFactory } from '@/modules/core/repositories/server'
import {
getStreamFactory,
Expand All @@ -49,6 +56,7 @@ import {
getUserStreamsCountFactory
} from '@/modules/core/repositories/streams'
import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/users'
import { createNewProjectFactory } from '@/modules/core/services/projects'
import {
getRateLimitResult,
isRateLimitBreached
Expand All @@ -68,7 +76,11 @@ import {
} from '@/modules/core/services/streams/management'
import { createOnboardingStreamFactory } from '@/modules/core/services/streams/onboarding'
import { getOnboardingBaseProjectFactory } from '@/modules/cross-server-sync/services/onboardingProject'
import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector'
import {
getDb,
getProjectDbClient,
getValidDefaultProjectRegionKey
} from '@/modules/multiregion/utils/dbSelector'
import {
deleteAllResourceInvitesFactory,
findUserByTargetFactory,
Expand Down Expand Up @@ -283,10 +295,23 @@ export = {
throw new RateLimitError(rateLimitResult)
}

const project = await createStreamReturnRecord({
const regionKey = await getValidDefaultProjectRegionKey()
const projectDb = await getDb({ regionKey })

const createNewProject = createNewProjectFactory({
storeProject: storeProjectFactory({ db: projectDb }),
getProject: getProjectFactory({ db }),
deleteProject: deleteProjectFactory({ db: projectDb }),
storeModel: storeModelFactory({ db: projectDb }),
// THIS MUST GO TO THE MAIN DB
storeProjectRole: storeProjectRoleFactory({ db }),
projectsEventsEmitter: ProjectsEmitter.emit
})

const project = await createNewProject({
...(args.input || {}),
ownerId: context.userId!,
ownerResourceAccessRules: context.resourceAccessRules
regionKey
})

return project
Expand Down
7 changes: 6 additions & 1 deletion packages/server/modules/multiregion/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { moduleLogger } from '@/logging/logging'
import { initializeRegisteredRegionClients as initDb } from '@/modules/multiregion/utils/dbSelector'
import {
getValidDefaultProjectRegionKey,
initializeRegisteredRegionClients as initDb
} from '@/modules/multiregion/utils/dbSelector'
import { isMultiRegionEnabled } from '@/modules/multiregion/helpers'
import { SpeckleModule } from '@/modules/shared/helpers/typeHelper'
import {
Expand All @@ -18,6 +21,8 @@ const multiRegion: SpeckleModule = {

// Init registered region clients
await initDb()
// validate default project region key
await getValidDefaultProjectRegionKey()

const isBlobStorageEnabled = isMultiRegionBlobStorageEnabled()
if (isBlobStorageEnabled) {
Expand Down
5 changes: 5 additions & 0 deletions packages/server/modules/multiregion/regionConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,8 @@ export const getMainRegionConfig = async (): Promise<MainRegionConfig> => {
export const getAvailableRegionConfig: GetAvailableRegionConfig = async () => {
return (await getMultiRegionConfig()).regions
}

export const getDefaultProjectRegionKey = async (): Promise<string | null> => {
const defaultRegionKey = (await getMultiRegionConfig()).defaultProjectRegionKey
return defaultRegionKey ?? null
}
21 changes: 20 additions & 1 deletion packages/server/modules/multiregion/utils/dbSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import {
} from '@/modules/multiregion/services/projectRegion'
import { Knex } from 'knex'
import { getRegionFactory } from '@/modules/multiregion/repositories'
import { DatabaseError } from '@/modules/shared/errors'
import { DatabaseError, MisconfiguredEnvironmentError } from '@/modules/shared/errors'
import { configureClient } from '@/knexfile'
import { InitializeRegion } from '@/modules/multiregion/domain/operations'
import {
getAvailableRegionConfig,
getDefaultProjectRegionKey,
getMainRegionConfig
} from '@/modules/multiregion/regionConfig'
import { ensureError, MaybeNullOrUndefined } from '@speckle/shared'
Expand Down Expand Up @@ -72,6 +73,24 @@ export const getProjectDbClient: GetProjectDb = async ({ projectId }) => {
return await getter({ projectId })
}

// the default region key is a config value, we're caching this globally
let defaultRegionKeyCache: string | null | undefined = undefined

export const getValidDefaultProjectRegionKey = async (): Promise<string | null> => {
if (defaultRegionKeyCache !== undefined) return defaultRegionKeyCache
const defaultRegionKey = await getDefaultProjectRegionKey()

if (!defaultRegionKey) return defaultRegionKey
const registeredRegionClients = await getRegisteredRegionClients()
if (!(defaultRegionKey in registeredRegionClients))
throw new MisconfiguredEnvironmentError(
`There is no region client registered for the default region key ${defaultRegionKey} `
)

defaultRegionKeyCache = defaultRegionKey
return defaultRegionKey
}

type RegionClients = Record<string, Knex>
let registeredRegionClients: RegionClients | undefined = undefined

Expand Down
8 changes: 6 additions & 2 deletions packages/server/modules/workspaces/services/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ import {
} from '@/modules/core/domain/streams/operations'
import { ProjectNotFoundError } from '@/modules/core/errors/projects'
import { WorkspaceProjectCreateInput } from '@/test/graphql/generated/graphql'
import { getDb } from '@/modules/multiregion/utils/dbSelector'
import {
getDb,
getValidDefaultProjectRegionKey
} from '@/modules/multiregion/utils/dbSelector'
import { createNewProjectFactory } from '@/modules/core/services/projects'
import {
deleteProjectFactory,
Expand Down Expand Up @@ -265,7 +268,8 @@ export const createWorkspaceProjectFactory =
const workspaceDefaultRegion = await deps.getDefaultRegion({
workspaceId: input.workspaceId
})
const regionKey = workspaceDefaultRegion?.key
const regionKey =
workspaceDefaultRegion?.key ?? (await getValidDefaultProjectRegionKey())
const projectDb = await getDb({ regionKey })
const db = mainDb

Expand Down
3 changes: 2 additions & 1 deletion packages/shared/src/environment/multiRegionConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ const regionConfigSchema = z.object({

const multiRegionConfigSchema = z.object({
main: regionConfigSchema,
regions: z.record(z.string(), regionConfigSchema)
regions: z.record(z.string(), regionConfigSchema),
defaultProjectRegionKey: z.string().min(3).nullish()
})

export type MultiRegionConfig = z.infer<typeof multiRegionConfigSchema>
Expand Down

0 comments on commit db8de11

Please sign in to comment.