Skip to content

Commit

Permalink
feat(multi-region): define the default region to create projects in
Browse files Browse the repository at this point in the history
  • Loading branch information
iainsproat committed Jan 8, 2025
1 parent be51d29 commit 0e3465b
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 7 deletions.
12 changes: 9 additions & 3 deletions packages/server/modules/core/graph/resolvers/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
} from '@/modules/core/services/streams/access'
import { cloneStreamFactory } from '@/modules/core/services/streams/clone'
import {
createStreamInDefaultRegionFactory,
createStreamReturnRecordFactory,
deleteStreamAndNotifyFactory,
updateStreamAndNotifyFactory,
Expand Down Expand Up @@ -117,7 +118,10 @@ const createStreamReturnRecord = createStreamReturnRecordFactory({
}),
getUsers
}),
createStream: createStreamFactory({ db }),
createStream: await createStreamInDefaultRegionFactory({
//FIXME this is a promise
createStreamFactory
}),
createBranch: createBranchFactory({ db }),
projectsEventsEmitter: ProjectsEmitter.emit
})
Expand Down Expand Up @@ -158,7 +162,10 @@ const cloneStream = cloneStreamFactory({
getUser,
newProjectDb: db,
sourceProjectDb: db,
createStream: createStreamFactory({ db }),
createStream: await createStreamInDefaultRegionFactory({
//FIXME this is a promise
createStreamFactory
}),
insertCommits: insertCommitsFactory({ db }),
getBatchedStreamCommits: getBatchedStreamCommitsFactory({ db }),
insertStreamCommits: insertStreamCommitsFactory({ db }),
Expand Down Expand Up @@ -277,7 +284,6 @@ export = {
})
return await updateStreamAndNotify(update, userId!, resourceAccessRules)
},
// This one is only used outside of a workspace, so the project is always created in the main db
async create(_parent, args, context) {
const rateLimitResult = await getRateLimitResult('STREAM_CREATE', context.userId!)
if (isRateLimitBreached(rateLimitResult)) {
Expand Down
16 changes: 16 additions & 0 deletions packages/server/modules/core/services/streams/management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ import {
AddStreamUpdatedActivity
} from '@/modules/activitystream/domain/operations'
import { LogicError } from '@/modules/shared/errors'
import { Knex } from 'knex'
import { getDb } from '@/modules/multiregion/utils/dbSelector'
import { isMultiRegionEnabled } from '@/modules/multiregion/helpers'
import { getDefaultProjectRegionKey } from '@/modules/multiregion/utils/regionSelector'

export const createStreamInDefaultRegionFactory = async (deps: {
createStreamFactory: (deps: { db: Knex }) => StoreStream
}): Promise<StoreStream> => {
let regionKey = null
if (isMultiRegionEnabled()) {
//FIXME is adding this check here the correct location?
regionKey = await getDefaultProjectRegionKey()
}
const db = await getDb({ regionKey }) //FIXME does this work if multi-region disabled?
return deps.createStreamFactory({ db })
}

export const createStreamReturnRecordFactory =
(deps: {
Expand Down
1 change: 1 addition & 0 deletions packages/server/modules/multiregion/regionConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const getMultiRegionConfig = async (): Promise<MultiRegionConfig> => {
// Only for non region enabled dev envs
const emptyReturn = (): MultiRegionConfig => ({
main: {
isDefaultProjectStore: true,
postgres: { connectionUri: '' },
blobStorage: {
accessKey: '',
Expand Down
18 changes: 18 additions & 0 deletions packages/server/modules/multiregion/utils/regionSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,21 @@ export const getProjectRegionKey: GetProjectRegionKey = async ({ projectId }) =>

return await cachedProjectRegionKeyResolver({ projectId })
}

export const getDefaultProjectRegionKey = async () => {
const registeredRegionConfigs = await getRegisteredRegionConfigs()
if (!registeredRegionConfigs) {
return null
}
const defaultProjectStoreRegion = Object.entries(registeredRegionConfigs)
.filter(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
([_, config]) => config.isDefaultProjectStore
)
.pop()
if (!defaultProjectStoreRegion) {
return null
}

return defaultProjectStoreRegion[0]
}
24 changes: 20 additions & 4 deletions packages/shared/src/environment/multiRegionConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { Knex, knex } from 'knex'
import { Logger } from 'pino'

const regionConfigSchema = z.object({
isDefaultProjectStore: z
.boolean()
.describe(
'Indicates that this is the default region in which projects should be created.'
)
.optional()
.default(false),
postgres: z.object({
connectionUri: z
.string()
Expand Down Expand Up @@ -39,10 +46,19 @@ const regionConfigSchema = z.object({
})
})

const multiRegionConfigSchema = z.object({
main: regionConfigSchema,
regions: z.record(z.string(), regionConfigSchema)
})
const multiRegionConfigSchema = z
.object({
main: regionConfigSchema,
regions: z.record(z.string(), regionConfigSchema)
})
.refine(
(input) =>
Object.values(input.regions).filter((region) => region.isDefaultProjectStore)
.length +
(input.main.isDefaultProjectStore ? 1 : 0) ===
1,
'Only one region can be the default project store'
)

export type MultiRegionConfig = z.infer<typeof multiRegionConfigSchema>
export type MainRegionConfig = MultiRegionConfig['main']
Expand Down

0 comments on commit 0e3465b

Please sign in to comment.