Fixes; initial migrator work
This commit is contained in:
parent
4bdd7a1e3b
commit
10dbbcd3ff
|
@ -1,6 +1,6 @@
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import { ChannelStateSizeError,
|
import { ChannelStateSizeError,
|
||||||
ChannelDataNotFoundError } from '../errors';
|
ChannelNotFoundError } from '../errors';
|
||||||
import db from '../database';
|
import db from '../database';
|
||||||
import Logger from '../logger';
|
import Logger from '../logger';
|
||||||
|
|
||||||
|
@ -25,6 +25,16 @@ function queryAsync(query, substitutions) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildUpdateQuery(numEntries) {
|
||||||
|
const values = [];
|
||||||
|
for (let i = 0; i < numEntries; i++) {
|
||||||
|
values.push('(?, ?, ?)');
|
||||||
|
}
|
||||||
|
|
||||||
|
return `INSERT INTO channel_data VALUES ${values.join(', ')} ` +
|
||||||
|
'ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)';
|
||||||
|
}
|
||||||
|
|
||||||
export class DatabaseStore {
|
export class DatabaseStore {
|
||||||
load(channelName) {
|
load(channelName) {
|
||||||
return queryAsync(QUERY_CHANNEL_ID_FOR_NAME, [channelName]).then((rows) => {
|
return queryAsync(QUERY_CHANNEL_ID_FOR_NAME, [channelName]).then((rows) => {
|
||||||
|
@ -55,17 +65,16 @@ export class DatabaseStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalSize = 0;
|
let totalSize = 0;
|
||||||
|
let rowCount = 0;
|
||||||
const id = rows[0].id;
|
const id = rows[0].id;
|
||||||
const substitutions = [];
|
const substitutions = [];
|
||||||
for (const key of Object.keys(data)) {
|
for (const key of Object.keys(data)) {
|
||||||
|
rowCount++;
|
||||||
const value = JSON.stringify(data[key]);
|
const value = JSON.stringify(data[key]);
|
||||||
totalSize += value.length;
|
totalSize += value.length;
|
||||||
substitutions.push([
|
substitutions.push(id);
|
||||||
id,
|
substitutions.push(key);
|
||||||
key,
|
substitutions.push(value);
|
||||||
value,
|
|
||||||
value // Extra substitution var necessary for ON DUPLICATE KEY UPDATE
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalSize > SIZE_LIMIT) {
|
if (totalSize > SIZE_LIMIT) {
|
||||||
|
@ -75,9 +84,7 @@ export class DatabaseStore {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.map(substitutions, entry => {
|
return queryAsync(buildUpdateQuery(rowCount), substitutions);
|
||||||
return queryAsync(QUERY_UPDATE_CHANNEL_DATA, entry);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { ChannelStateSizeError } from '../errors';
|
||||||
|
|
||||||
const readFileAsync = Promise.promisify(fs.readFile);
|
const readFileAsync = Promise.promisify(fs.readFile);
|
||||||
const writeFileAsync = Promise.promisify(fs.writeFile);
|
const writeFileAsync = Promise.promisify(fs.writeFile);
|
||||||
|
const readdirAsync = Promise.promisify(fs.readdir);
|
||||||
const statAsync = Promise.promisify(stat);
|
const statAsync = Promise.promisify(stat);
|
||||||
const SIZE_LIMIT = 1048576;
|
const SIZE_LIMIT = 1048576;
|
||||||
const CHANDUMP_DIR = path.resolve(__dirname, '..', '..', 'chandump');
|
const CHANDUMP_DIR = path.resolve(__dirname, '..', '..', 'chandump');
|
||||||
|
@ -48,4 +49,8 @@ export class FileStore {
|
||||||
|
|
||||||
return writeFileAsync(filename, fileContents);
|
return writeFileAsync(filename, fileContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listChannels() {
|
||||||
|
return readdirAsync(CHANDUMP_DIR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
74
src/channel-storage/migrator.js
Normal file
74
src/channel-storage/migrator.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import Config from '../config';
|
||||||
|
import Promise from 'bluebird';
|
||||||
|
import db from '../database';
|
||||||
|
import { FileStore } from './filestore';
|
||||||
|
import { DatabaseStore } from './dbstore';
|
||||||
|
|
||||||
|
const QUERY_CHANNEL_NAMES = 'SELECT name FROM channels WHERE 1';
|
||||||
|
|
||||||
|
function queryAsync(query, substitutions) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.query(query, substitutions, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
if (!(err instanceof Error)) {
|
||||||
|
err = new Error(err);
|
||||||
|
}
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixOldChandump(data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrate(src, dest) {
|
||||||
|
return src.listChannels().then(names => {
|
||||||
|
return Promise.reduce(names, (_, name) => {
|
||||||
|
// A long time ago there was a bug where CyTube would save a different
|
||||||
|
// chandump depending on the capitalization of the channel name in the URL.
|
||||||
|
// This was fixed, but there are still some really old chandumps with
|
||||||
|
// uppercase letters in the name.
|
||||||
|
//
|
||||||
|
// If another chandump exists which is all lowercase, then that one is
|
||||||
|
// canonical. Otherwise, it's safe to just lowercase the name and convert
|
||||||
|
// it.
|
||||||
|
if (name !== name.toLowerCase()) {
|
||||||
|
if (names.indexOf(name.toLowerCase()) >= 0) {
|
||||||
|
return Promise.resolve();
|
||||||
|
} else {
|
||||||
|
name = name.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return src.load(name).then(data => {
|
||||||
|
data = fixOldChandump(data);
|
||||||
|
return dest.save(name, data);
|
||||||
|
}).then(() => {
|
||||||
|
console.log(`Migrated /r/${name}`);
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(`Failed to migrate /r/${name}: ${err.stack}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
Config.load('config.yaml');
|
||||||
|
db.init();
|
||||||
|
const src = new FileStore();
|
||||||
|
const dest = new DatabaseStore();
|
||||||
|
|
||||||
|
migrate(src, dest).then(() => {
|
||||||
|
console.log('Migration complete');
|
||||||
|
process.exit(0);
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(`Migration failed: ${err.stack}`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
Loading…
Reference in a new issue