Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
7 changes: 7 additions & 0 deletions src/backup/backupRestorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default class BackupRestorer extends CommandBase {
private statusGetter: BackupRestoreStatusGetter;
private waitForCompletion?: boolean;
private config?: RestoreConfig;
private overwriteAlias?: boolean;

constructor(client: Connection, statusGetter: BackupRestoreStatusGetter) {
super(client);
Expand Down Expand Up @@ -65,6 +66,11 @@ export default class BackupRestorer extends CommandBase {
return this;
}

withOverwriteAlias(overwriteAlias: boolean) {
this.overwriteAlias = overwriteAlias;
return this;
}

withConfig(cfg: RestoreConfig) {
this.config = cfg;
return this;
Expand All @@ -89,6 +95,7 @@ export default class BackupRestorer extends CommandBase {
config: this.config,
include: this.includeClassNames,
exclude: this.excludeClassNames,
overwriteAlias: this.overwriteAlias,
} as BackupRestoreRequest;

if (this.waitForCompletion) {
Expand Down
9 changes: 6 additions & 3 deletions src/collections/backup/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@
if (args.excludeCollections) {
builder = builder.withExcludeClassNames(...args.excludeCollections);
}
if (args.config?.overwriteAlias) {
builder = builder.withOverwriteAlias(args.config?.overwriteAlias);
}
if (args.config) {
builder = builder.withConfig({
CPUPercentage: args.config.cpuPercentage,
Expand Down Expand Up @@ -197,9 +200,9 @@
}
return status
? {
...parseResponse(res),
...status,
}
...parseResponse(res),

Check failure on line 203 in src/collections/backup/client.ts

View workflow job for this annotation

GitHub Actions / checks

Insert `··`
...status,

Check failure on line 204 in src/collections/backup/client.ts

View workflow job for this annotation

GitHub Actions / checks

Insert `··`
}

Check failure on line 205 in src/collections/backup/client.ts

View workflow job for this annotation

GitHub Actions / checks

Insert `··`
: parseResponse(res);
},
};
Expand Down
1 change: 0 additions & 1 deletion src/collections/backup/collection.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Backend } from '../../backup/index.js';
import Connection from '../../connection/index.js';
import { WeaviateInvalidInputError } from '../../errors.js';
import { backup } from './client.js';
import { BackupReturn, BackupStatusArgs, BackupStatusReturn } from './types.js';

Expand Down
100 changes: 100 additions & 0 deletions src/collections/backup/integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
/* eslint-disable no-await-in-loop */
import { Backend } from '../../backup/index.js';

Check failure on line 4 in src/collections/backup/integration.test.ts

View workflow job for this annotation

GitHub Actions / checks

Replace `Backend·}·from·'../../backup/index.js` with `requireAtLeast·}·from·'../../../test/version`
import weaviate, { Collection, WeaviateClient } from '../../index.js';

Check failure on line 5 in src/collections/backup/integration.test.ts

View workflow job for this annotation

GitHub Actions / checks

Replace `weaviate,·{·Collection,·WeaviateClient·}·from·'../..` with `{·Backend·}·from·'../../backup`
import { requireAtLeast } from '../../../test/version';

Check failure on line 6 in src/collections/backup/integration.test.ts

View workflow job for this annotation

GitHub Actions / checks

Replace `requireAtLeast·}·from·'../../../test/version` with `WeaviateBackupFailed·}·from·'../../errors.js`
import { WeaviateBackupFailed } from '../../errors.js';

Check failure on line 7 in src/collections/backup/integration.test.ts

View workflow job for this annotation

GitHub Actions / checks

Replace `{·WeaviateBackupFailed·}·from·'../../errors` with `weaviate,·{·Collection,·WeaviateClient·}·from·'../../index`

// These must run sequentially because Weaviate is not capable of running multiple backups at the same time
describe('Integration testing of backups', () => {
Expand Down Expand Up @@ -88,6 +90,23 @@
backend: res.backend as 'filesystem',
});
expect(status).not.toBe('SUCCESS'); // can be 'STARTED' or 'TRANSFERRING' depending on the speed of the test machine

// wait to complete so that other tests can run without colliding with Weaviate's lack of simultaneous backups
let wait = true;
while (wait) {
const { status, error } = await collection.backup.getCreateStatus({
backupId: res.id as string,
backend: res.backend as Backend,
});
if (status === 'SUCCESS') {
wait = false;
}
if (status === 'FAILED') {
throw new Error(`Backup creation failed: ${error}`);
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}

return collection;
};

Expand All @@ -98,6 +117,87 @@
.then(getCollection)
.then(testCollectionWaitForCompletion)
.then(testCollectionNoWaitForCompletion));

requireAtLeast(1, 32, 3).describe('overwrite alias', () => {

Check failure on line 122 in src/collections/backup/integration.test.ts

View workflow job for this annotation

GitHub Actions / checks

Delete `⏎`
test('overwriteAlias=true', async () => {
const client = await clientPromise;

const things = await client.collections.create({ name: "ThingsTrue" });

Check failure on line 126 in src/collections/backup/integration.test.ts

View workflow job for this annotation

GitHub Actions / checks

Replace `"ThingsTrue"` with `'ThingsTrue'`
await client.alias.create({ collection: things.name, alias: `${things.name}Alias` });

const backup = await client.backup.create({
backend: 'filesystem',
backupId: randomBackupId(),
includeCollections: [things.name],
waitForCompletion: true,
});

await client.collections.delete(things.name);
await client.alias.delete(`${things.name}Alias`);

// Change alias to point to a different collection
const inventory = await client.collections.create({ name: "InventoryTrue" });

Check failure on line 140 in src/collections/backup/integration.test.ts

View workflow job for this annotation

GitHub Actions / checks

Replace `"InventoryTrue"` with `'InventoryTrue'`
await client.alias.create({ collection: inventory.name, alias: `${things.name}Alias` });


// Restore backup with overwriteAlias=true
await client.backup.restore({
backend: 'filesystem',
backupId: backup.id,
includeCollections: [things.name],
waitForCompletion: true,
config: { overwriteAlias: true },
});

// Assert: alias points to the original collection
const alias = await client.alias.get(`${things.name}Alias`);
expect(alias.collection).toEqual(things.name);
});

test('overwriteAlias=false', async () => {
const client = await clientPromise;

const things = await client.collections.create({ name: "ThingsFalse" });
await client.alias.create({ collection: things.name, alias: `${things.name}Alias` });

const backup = await client.backup.create({
backend: 'filesystem',
backupId: randomBackupId(),
includeCollections: [things.name],
waitForCompletion: true,
});

await client.collections.delete(things.name);
await client.alias.delete(`${things.name}Alias`);

// Change alias to point to a different collection
const inventory = await client.collections.create({ name: "InventoryFalse" });
await client.alias.create({ collection: inventory.name, alias: `${things.name}Alias` });


// Restore backup with overwriteAlias=true
const restored = client.backup.restore({
backend: 'filesystem',
backupId: backup.id,
includeCollections: [things.name],
waitForCompletion: true,
config: { overwriteAlias: false },
});

// Assert: fails with "alias already exists" error
await expect(restored).rejects.toThrowError(WeaviateBackupFailed);
});

it('cleanup', async () => {
await clientPromise.then(async c => {
await Promise.all(["ThingsTrue", "ThingsFalse", "InventoryTrue", "InventoryFalse"]
.map(name => c.collections.delete(name).catch(e => { })));
await c.alias.delete("ThingsFalseAlias").catch(e => { });
await c.alias.delete("ThingsTrueAlias").catch(e => { });
});
})
});
});

function randomBackupId() {
Expand Down
2 changes: 2 additions & 0 deletions src/collections/backup/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export type BackupConfigCreate = {
export type BackupConfigRestore = {
/** The percentage of CPU to use for the backuop restoration job. */
cpuPercentage?: number;
/**Allows ovewriting the collection alias if there is a conflict. */
overwriteAlias?: boolean;
};

/** The arguments required to create and restore backups. */
Expand Down
Loading