/** * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import("sqlbase.sqlobj"); import("etherpad.pad.model"); import("etherpad.pad.easysync2migration"); import("sqlbase.sqlcommon"); import("sqlbase.sqlobj"); import("etherpad.log"); import("etherpad.pne.pne_utils"); jimport("java.util.concurrent.ConcurrentHashMap"); jimport("java.lang.System"); jimport("java.util.ArrayList"); jimport("java.util.Collections"); function onStartup() { if (! appjet.cache.pad_migrations) { appjet.cache.pad_migrations = {}; } // this part can be removed when all pads are migrated on pad.spline.inf.fu-berlin.de //if (! pne_utils.isPNE()) { // System.out.println("Building cache for live migrations..."); // initLiveMigration(); //} } function initLiveMigration() { if (! appjet.cache.pad_migrations) { appjet.cache.pad_migrations = {}; } appjet.cache.pad_migrations.doingAnyLiveMigrations = true; appjet.cache.pad_migrations.doingBackgroundLiveMigrations = true; appjet.cache.pad_migrations.padMap = new ConcurrentHashMap(); // presence of a pad in padMap indicates migration is needed var padMap = _padMap(); var migrationsNeeded = sqlobj.selectMulti("PAD_SQLMETA", {version: 1}); migrationsNeeded.forEach(function(obj) { padMap.put(String(obj.id), {from: obj.version}); }); } function _padMap() { return appjet.cache.pad_migrations.padMap; } function _doingItLive() { return !! appjet.cache.pad_migrations.doingAnyLiveMigrations; } function checkPadStatus(padId) { if (! _doingItLive()) { return "ready"; } var info = _padMap().get(padId); if (! info) { return "ready"; } else if (info.migrating) { return "migrating"; } else { return "oldversion"; } } function ensureMigrated(padId, async) { if (! _doingItLive()) { return false; } var info = _padMap().get(padId); if (! info) { // pad is up-to-date return false; } else if (async && info.migrating) { // pad is already being migrated, don't wait on the lock return false; } return model.doWithPadLock(padId, function() { // inside pad lock... var info = _padMap().get(padId); if (!info) { return false; } // migrate from version 1 to version 2 in a transaction var migrateSucceeded = false; try { info.migrating = true; log.info("Migrating pad "+padId+" from version 1 to version 2..."); var success = false; var whichTry = 1; while ((! success) && whichTry <= 3) { success = sqlcommon.inTransaction(function() { try { easysync2migration.migratePad(padId); sqlobj.update("PAD_SQLMETA", {id: padId}, {version: 2}); return true; } catch (e if (e.toString().indexOf("try restarting transaction") >= 0)) { whichTry++; return false; } }); if (! success) { java.lang.Thread.sleep(Math.floor(Math.random()*200)); } } if (! success) { throw new Error("too many retries"); } migrateSucceeded = true; log.info("Migrated pad "+padId+"."); _padMap().remove(padId); } finally { info.migrating = false; if (! migrateSucceeded) { log.info("Migration failed for pad "+padId+"."); throw new Error("Migration failed for pad "+padId+"."); } } return true; }); } function numUnmigratedPads() { if (! _doingItLive()) { return 0; } return _padMap().size(); } ////////// BACKGROUND MIGRATIONS function _logPadMigration(runnerId, padNumber, padTotal, timeMs, fourCharResult, padId) { log.custom("pad_migrations", { runnerId: runnerId, padNumber: Math.round(padNumber+1), padTotal: Math.round(padTotal), timeMs: Math.round(timeMs), fourCharResult: fourCharResult, padId: padId}); } function _getNeededMigrationsArrayList(filter) { var L = new ArrayList(_padMap().keySet()); for(var i=L.size()-1; i>=0; i--) { if (! filter(String(L.get(i)))) { L.remove(i); } } return L; } function runBackgroundMigration(residue, modulus, runnerId) { var L = _getNeededMigrationsArrayList(function(padId) { return (padId.charCodeAt(0) % modulus) == residue; }); Collections.shuffle(L); var totalPads = L.size(); for(var i=0;i