Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(regions): move project branches and commits #3843

Merged
merged 25 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4d5f96b
feat(regions): repo functions for copying project branches and commits
cdriesler Jan 12, 2025
e5d04e5
chore(regions): wire up move to resolver
cdriesler Jan 13, 2025
f64a8bb
chore(regions): successful basic test of project region change
cdriesler Jan 14, 2025
f1a82e6
fix(regions): sabrina carpenter please please please
cdriesler Jan 16, 2025
ec0df3c
Merge remote-tracking branch 'origin' into chuck/web-2433-move-projec…
cdriesler Jan 16, 2025
88bafb2
fix(regions): repair multiregion test setup
cdriesler Jan 22, 2025
34a68a8
Merge remote-tracking branch 'origin' into chuck/web-2433-move-projec…
cdriesler Jan 22, 2025
1fb3b43
chore(regions): appease ts
cdriesler Jan 22, 2025
54b61bf
chore(multiregion): update test multiregion config
cdriesler Jan 22, 2025
de8d78a
chore(multiregion): fix test docker config and test
cdriesler Jan 22, 2025
ca85039
chore(multiregion): use transaction
cdriesler Jan 23, 2025
201fe2e
chore(multiregion): maybe this will work
cdriesler Jan 23, 2025
08b53cc
fix(multiregion): drop subs synchronously
cdriesler Jan 24, 2025
8edde64
chore(multiregion): desperate test logs
cdriesler Jan 24, 2025
afc06d7
chore(multiregion): somehow that worked?
cdriesler Jan 24, 2025
6d6f800
chore(multiregion): add load-bearing log statement
cdriesler Jan 24, 2025
a27b97a
chore(multiregion): move services
cdriesler Jan 24, 2025
6a0fadc
fix(multiregion): test drop waits
cdriesler Jan 24, 2025
969f8b6
Merge branch 'main' into chuck/web-2433-move-project-branches-and-com…
cdriesler Jan 27, 2025
361960c
Merge branch 'main' into chuck/web-2433-move-project-branches-and-com…
cdriesler Feb 2, 2025
0811916
chore(regions): fix import
cdriesler Feb 3, 2025
b48721e
chore(regions): make test a bit more thorough for good measure
cdriesler Feb 3, 2025
04edd9c
fix(regions): speed up inserts
cdriesler Feb 6, 2025
f830056
Merge branch 'main' into chuck/web-2433-move-project-branches-and-com…
cdriesler Feb 7, 2025
c1d04e7
fix(regions): ignore workspace conflict on move
cdriesler Feb 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -598,16 +598,24 @@ jobs:
POSTGRES_PASSWORD: speckle
POSTGRES_USER: speckle
command: -c 'max_connections=1000' -c 'port=5433' -c 'wal_level=logical'
- image: 'speckle/speckle-postgres'
environment:
POSTGRES_DB: speckle2_test
POSTGRES_PASSWORD: speckle
POSTGRES_USER: speckle
command: -c 'max_connections=1000' -c 'port=5434' -c 'wal_level=logical'
- image: 'minio/minio'
command: server /data --console-address ":9001" --address "0.0.0.0:9000"
- image: 'minio/minio'
command: server /data --console-address ":9021" --address "0.0.0.0:9020"
- image: 'minio/minio'
command: server /data --console-address ":9041" --address "0.0.0.0:9040"
environment:
# Same as test-server:
NODE_ENV: test
DATABASE_URL: 'postgres://speckle:[email protected]:5432/speckle2_test'
PGDATABASE: speckle2_test
POSTGRES_MAX_CONNECTIONS_SERVER: 20
POSTGRES_MAX_CONNECTIONS_SERVER: 50
PGUSER: speckle
SESSION_SECRET: 'keyboard cat'
STRATEGY_LOCAL: 'true'
Expand Down
13 changes: 13 additions & 0 deletions .circleci/multiregion.test-ci.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@
"endpoint": "http://127.0.0.1:9020",
"s3Region": "us-east-1"
}
},
"region2": {
"postgres": {
"connectionUri": "postgresql://speckle:[email protected]:5434/speckle2_test"
},
"blobStorage": {
"accessKey": "minioadmin",
"secretKey": "minioadmin",
"bucket": "speckle-server",
"createBucketIfNotExists": true,
"endpoint": "http://127.0.0.1:9040",
"s3Region": "us-east-1"
}
}
}
}
28 changes: 28 additions & 0 deletions docker-compose-deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ services:
ports:
- '127.0.0.1:5401:5432'

postgres-region2:
build:
context: .
dockerfile: utils/postgres/Dockerfile
restart: always
environment:
POSTGRES_DB: speckle
POSTGRES_USER: speckle
POSTGRES_PASSWORD: speckle
volumes:
- postgres-region2-data:/var/lib/postgresql/data/
- ./setup/db/10-docker_postgres_init.sql:/docker-entrypoint-initdb.d/10-docker_postgres_init.sql
- ./setup/db/11-docker_postgres_keycloack_init.sql:/docker-entrypoint-initdb.d/11-docker_postgres_keycloack_init.sql
ports:
- '127.0.0.1:5402:5432'

redis:
image: 'redis:7-alpine'
restart: always
Expand Down Expand Up @@ -62,6 +78,16 @@ services:
- '127.0.0.1:9020:9000'
- '127.0.0.1:9021:9001'

minio-region2:
image: 'minio/minio'
command: server /data --console-address ":9001"
restart: always
volumes:
- minio-region2-data:/data
ports:
- '127.0.0.1:9040:9000'
- '127.0.0.1:9041:9001'

# Local OIDC provider for testing
keycloak:
image: quay.io/keycloak/keycloak:25.0
Expand Down Expand Up @@ -133,8 +159,10 @@ services:
volumes:
postgres-data:
postgres-region1-data:
postgres-region2-data:
redis-data:
pgadmin-data:
redis_insight-data:
minio-data:
minio-region1-data:
minio-region2-data:
12 changes: 12 additions & 0 deletions packages/frontend-2/lib/common/generated/gql/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4587,6 +4587,11 @@ export type WorkspaceProjectInviteCreateInput = {
export type WorkspaceProjectMutations = {
__typename?: 'WorkspaceProjectMutations';
create: Project;
/**
* Update project region and move all regional data to new db.
* TODO: Currently performs all operations synchronously in request, should probably be scheduled.
*/
moveToRegion: Project;
moveToWorkspace: Project;
updateRole: Project;
};
Expand All @@ -4597,6 +4602,12 @@ export type WorkspaceProjectMutationsCreateArgs = {
};


export type WorkspaceProjectMutationsMoveToRegionArgs = {
projectId: Scalars['String']['input'];
regionKey: Scalars['String']['input'];
};


export type WorkspaceProjectMutationsMoveToWorkspaceArgs = {
projectId: Scalars['String']['input'];
workspaceId: Scalars['String']['input'];
Expand Down Expand Up @@ -8299,6 +8310,7 @@ export type WorkspacePlanFieldArgs = {
}
export type WorkspaceProjectMutationsFieldArgs = {
create: WorkspaceProjectMutationsCreateArgs,
moveToRegion: WorkspaceProjectMutationsMoveToRegionArgs,
moveToWorkspace: WorkspaceProjectMutationsMoveToWorkspaceArgs,
updateRole: WorkspaceProjectMutationsUpdateRoleArgs,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ extend type WorkspaceMutations {
"""
setDefaultRegion(workspaceId: String!, regionKey: String!): Workspace!
}

extend type WorkspaceProjectMutations {
"""
Update project region and move all regional data to new db.
TODO: Currently performs all operations synchronously in request, should probably be scheduled.
"""
moveToRegion(projectId: String!, regionKey: String!): Project!
}
6 changes: 0 additions & 6 deletions packages/server/modules/auth/tests/apps.graphql.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ const { getServerInfoFactory } = require('@/modules/core/repositories/server')
const { getEventBus } = require('@/modules/shared/services/eventBus')

let sendRequest
let server

const createAppToken = createAppTokenFactory({
storeApiToken: storeApiTokenFactory({ db }),
Expand Down Expand Up @@ -128,7 +127,6 @@ describe('GraphQL @apps-api', () => {

before(async () => {
const ctx = await beforeEachContext()
server = ctx.server
;({ sendRequest } = await initializeTestServer(ctx))
testUser = {
name: 'Dimitrie Stefanescu',
Expand Down Expand Up @@ -157,10 +155,6 @@ describe('GraphQL @apps-api', () => {
])}`
})

after(async () => {
await server.close()
})

let testAppId
let testApp

Expand Down
6 changes: 0 additions & 6 deletions packages/server/modules/auth/tests/auth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ const expect = chai.expect

let app
let sendRequest
let server

describe('Auth @auth', () => {
describe('Local authN & authZ (token endpoints)', () => {
Expand All @@ -160,7 +159,6 @@ describe('Auth @auth', () => {

before(async () => {
const ctx = await beforeEachContext()
server = ctx.server
app = ctx.app
;({ sendRequest } = await initializeTestServer(ctx))

Expand All @@ -173,10 +171,6 @@ describe('Auth @auth', () => {
)
})

after(async () => {
await server.close()
})

it('Should register a new user (speckle frontend)', async () => {
await request(app)
.post('/auth/local/register?challenge=test')
Expand Down
12 changes: 12 additions & 0 deletions packages/server/modules/core/graph/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4609,6 +4609,11 @@ export type WorkspaceProjectInviteCreateInput = {
export type WorkspaceProjectMutations = {
__typename?: 'WorkspaceProjectMutations';
create: Project;
/**
* Update project region and move all regional data to new db.
* TODO: Currently performs all operations synchronously in request, should probably be scheduled.
*/
moveToRegion: Project;
moveToWorkspace: Project;
updateRole: Project;
};
Expand All @@ -4619,6 +4624,12 @@ export type WorkspaceProjectMutationsCreateArgs = {
};


export type WorkspaceProjectMutationsMoveToRegionArgs = {
projectId: Scalars['String']['input'];
regionKey: Scalars['String']['input'];
};


export type WorkspaceProjectMutationsMoveToWorkspaceArgs = {
projectId: Scalars['String']['input'];
workspaceId: Scalars['String']['input'];
Expand Down Expand Up @@ -6943,6 +6954,7 @@ export type WorkspacePlanResolvers<ContextType = GraphQLContext, ParentType exte

export type WorkspaceProjectMutationsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['WorkspaceProjectMutations'] = ResolversParentTypes['WorkspaceProjectMutations']> = {
create?: Resolver<ResolversTypes['Project'], ParentType, ContextType, RequireFields<WorkspaceProjectMutationsCreateArgs, 'input'>>;
moveToRegion?: Resolver<ResolversTypes['Project'], ParentType, ContextType, RequireFields<WorkspaceProjectMutationsMoveToRegionArgs, 'projectId' | 'regionKey'>>;
moveToWorkspace?: Resolver<ResolversTypes['Project'], ParentType, ContextType, RequireFields<WorkspaceProjectMutationsMoveToWorkspaceArgs, 'projectId' | 'workspaceId'>>;
updateRole?: Resolver<ResolversTypes['Project'], ParentType, ContextType, RequireFields<WorkspaceProjectMutationsUpdateRoleArgs, 'input'>>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
Expand Down
2 changes: 1 addition & 1 deletion packages/server/modules/core/services/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const createNewProjectFactory =
const replicatedProject = await getProject({ projectId })
if (!replicatedProject) throw new StreamNotFoundError()
},
{ maxAttempts: 10 }
{ maxAttempts: 100 }
cdriesler marked this conversation as resolved.
Show resolved Hide resolved
)
} catch (err) {
if (err instanceof StreamNotFoundError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4590,6 +4590,11 @@ export type WorkspaceProjectInviteCreateInput = {
export type WorkspaceProjectMutations = {
__typename?: 'WorkspaceProjectMutations';
create: Project;
/**
* Update project region and move all regional data to new db.
* TODO: Currently performs all operations synchronously in request, should probably be scheduled.
*/
moveToRegion: Project;
moveToWorkspace: Project;
updateRole: Project;
};
Expand All @@ -4600,6 +4605,12 @@ export type WorkspaceProjectMutationsCreateArgs = {
};


export type WorkspaceProjectMutationsMoveToRegionArgs = {
projectId: Scalars['String']['input'];
regionKey: Scalars['String']['input'];
};


export type WorkspaceProjectMutationsMoveToWorkspaceArgs = {
projectId: Scalars['String']['input'];
workspaceId: Scalars['String']['input'];
Expand Down
13 changes: 11 additions & 2 deletions packages/server/modules/multiregion/utils/dbSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ const setUpUserReplication = async ({
try {
await from.public.raw(`CREATE PUBLICATION ${pubName} FOR TABLE users;`)
} catch (err) {
if (!(err instanceof Error))
if (!(err instanceof Error)) {
throw new DatabaseError(
'Could not create publication {pubName} when setting up user replication for region {regionName}',
from.public,
Expand All @@ -223,7 +223,16 @@ const setUpUserReplication = async ({
info: { pubName, regionName }
}
)
if (!err.message.includes('already exists')) throw err
}

const errorMessage = err.message

if (
!['already exists', 'violates unique constraint'].some((message) =>
errorMessage.includes(message)
)
cdriesler marked this conversation as resolved.
Show resolved Hide resolved
)
throw err
}

const fromUrl = new URL(
Expand Down
4 changes: 2 additions & 2 deletions packages/server/modules/shared/helpers/dbHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export async function* executeBatchedSelect<
>(
selectQuery: Knex.QueryBuilder<TRecord, TResult>,
options?: Partial<BatchedSelectOptions>
): AsyncGenerator<TResult, void, unknown> {
): AsyncGenerator<Awaited<typeof selectQuery>, void, unknown> {
const { batchSize = 100, trx } = options || {}

if (trx) selectQuery.transacting(trx)
Expand All @@ -34,7 +34,7 @@ export async function* executeBatchedSelect<
let currentOffset = 0
while (hasMorePages) {
const q = selectQuery.clone().offset(currentOffset)
const results = (await q) as TResult
const results = (await q) as Awaited<typeof selectQuery>

if (!results.length) {
hasMorePages = false
Expand Down
9 changes: 1 addition & 8 deletions packages/server/modules/stats/tests/stats.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
getTotalUserCountFactory
} from '@/modules/stats/repositories/index'
import { Scopes } from '@speckle/shared'
import { Server } from 'node:http'
import { db } from '@/db/knex'
import {
createCommitByBranchIdFactory,
Expand Down Expand Up @@ -194,8 +193,7 @@ describe('Server stats services @stats-services', function () {
})

describe('Server stats api @stats-api', function () {
let server: Server,
sendRequest: Awaited<ReturnType<typeof initializeTestServer>>['sendRequest']
let sendRequest: Awaited<ReturnType<typeof initializeTestServer>>['sendRequest']

const adminUser = {
name: 'Dimitrie',
Expand Down Expand Up @@ -233,7 +231,6 @@ describe('Server stats api @stats-api', function () {
before(async function () {
this.timeout(15000)
const ctx = await beforeEachContext()
server = ctx.server
;({ sendRequest } = await initializeTestServer(ctx))

adminUser.id = await createUser(adminUser)
Expand Down Expand Up @@ -263,10 +260,6 @@ describe('Server stats api @stats-api', function () {
await seedDb(params)
})

after(async function () {
await server.close()
})

it('Should not get stats if user is not admin', async () => {
const res = await sendRequest(adminUser.badToken, { query: fullQuery })
expect(res.body.errors).to.exist
Expand Down
20 changes: 2 additions & 18 deletions packages/server/modules/webhooks/tests/webhooks.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
const expect = require('chai').expect
const assert = require('assert')

const {
beforeEachContext,
initializeTestServer,
truncateTables
} = require('@/test/hooks')
const { beforeEachContext, initializeTestServer } = require('@/test/hooks')
const { noErrors } = require('@/test/helpers')
const { Scopes, Roles } = require('@speckle/shared')
const {
Expand All @@ -26,7 +22,6 @@ const {
deleteWebhookFactory,
dispatchStreamEventFactory
} = require('@/modules/webhooks/services/webhooks')
const { Users, Streams } = require('@/modules/core/dbSchema')
const {
getStreamFactory,
createStreamFactory,
Expand Down Expand Up @@ -166,7 +161,7 @@ const createPersonalAccessToken = createPersonalAccessTokenFactory({

describe('Webhooks @webhooks', () => {
const getWebhook = getWebhookByIdFactory({ db })
let server, sendRequest
let sendRequest

const userOne = {
name: 'User',
Expand All @@ -191,7 +186,6 @@ describe('Webhooks @webhooks', () => {

before(async () => {
const ctx = await beforeEachContext()
server = ctx.server
;({ sendRequest } = await initializeTestServer(ctx))

userOne.id = await createUser(userOne)
Expand All @@ -201,16 +195,6 @@ describe('Webhooks @webhooks', () => {
webhookOne.streamId = streamOne.id
})

after(async () => {
await truncateTables([
Users.name,
Streams.name,
'webhooks_config',
'webhooks_events'
])
await server.close()
})

describe('Create, Read, Update, Delete Webhooks', () => {
it('Should create a webhook', async () => {
webhookOne.id = await createWebhookFactory({
Expand Down
Loading
Loading