2
* Copyright 2009 Google Inc.
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS-IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
17
import("sqlbase.sqlobj");
18
import("etherpad.pad.model");
19
import("etherpad.pad.easysync2migration");
20
import("sqlbase.sqlcommon");
21
import("sqlbase.sqlobj");
22
import("etherpad.log");
23
import("etherpad.pne.pne_utils");
24
jimport("java.util.concurrent.ConcurrentHashMap");
25
jimport("java.lang.System");
26
jimport("java.util.ArrayList");
27
jimport("java.util.Collections");
29
function onStartup() {
30
if (! appjet.cache.pad_migrations) {
31
appjet.cache.pad_migrations = {};
34
// this part can be removed when all pads are migrated on etherpad.com
35
//if (! pne_utils.isPNE()) {
36
// System.out.println("Building cache for live migrations...");
37
// initLiveMigration();
41
function initLiveMigration() {
43
if (! appjet.cache.pad_migrations) {
44
appjet.cache.pad_migrations = {};
46
appjet.cache.pad_migrations.doingAnyLiveMigrations = true;
47
appjet.cache.pad_migrations.doingBackgroundLiveMigrations = true;
48
appjet.cache.pad_migrations.padMap = new ConcurrentHashMap();
50
// presence of a pad in padMap indicates migration is needed
51
var padMap = _padMap();
52
var migrationsNeeded = sqlobj.selectMulti("PAD_SQLMETA", {version: 1});
53
migrationsNeeded.forEach(function(obj) {
54
padMap.put(String(obj.id), {from: obj.version});
59
return appjet.cache.pad_migrations.padMap;
62
function _doingItLive() {
63
return !! appjet.cache.pad_migrations.doingAnyLiveMigrations;
66
function checkPadStatus(padId) {
67
if (! _doingItLive()) {
70
var info = _padMap().get(padId);
74
else if (info.migrating) {
82
function ensureMigrated(padId, async) {
83
if (! _doingItLive()) {
87
var info = _padMap().get(padId);
92
else if (async && info.migrating) {
93
// pad is already being migrated, don't wait on the lock
97
return model.doWithPadLock(padId, function() {
99
var info = _padMap().get(padId);
103
// migrate from version 1 to version 2 in a transaction
104
var migrateSucceeded = false;
106
info.migrating = true;
107
log.info("Migrating pad "+padId+" from version 1 to version 2...");
111
while ((! success) && whichTry <= 3) {
112
success = sqlcommon.inTransaction(function() {
114
easysync2migration.migratePad(padId);
115
sqlobj.update("PAD_SQLMETA", {id: padId}, {version: 2});
118
catch (e if (e.toString().indexOf("try restarting transaction") >= 0)) {
124
java.lang.Thread.sleep(Math.floor(Math.random()*200));
128
throw new Error("too many retries");
131
migrateSucceeded = true;
132
log.info("Migrated pad "+padId+".");
133
_padMap().remove(padId);
136
info.migrating = false;
137
if (! migrateSucceeded) {
138
log.info("Migration failed for pad "+padId+".");
139
throw new Error("Migration failed for pad "+padId+".");
146
function numUnmigratedPads() {
147
if (! _doingItLive()) {
151
return _padMap().size();
154
////////// BACKGROUND MIGRATIONS
156
function _logPadMigration(runnerId, padNumber, padTotal, timeMs, fourCharResult, padId) {
157
log.custom("pad_migrations", {
159
padNumber: Math.round(padNumber+1),
160
padTotal: Math.round(padTotal),
161
timeMs: Math.round(timeMs),
162
fourCharResult: fourCharResult,
166
function _getNeededMigrationsArrayList(filter) {
167
var L = new ArrayList(_padMap().keySet());
168
for(var i=L.size()-1; i>=0; i--) {
169
if (! filter(String(L.get(i)))) {
176
function runBackgroundMigration(residue, modulus, runnerId) {
177
var L = _getNeededMigrationsArrayList(function(padId) {
178
return (padId.charCodeAt(0) % modulus) == residue;
180
Collections.shuffle(L);
182
var totalPads = L.size();
183
for(var i=0;i<totalPads;i++) {
184
if (! appjet.cache.pad_migrations.doingBackgroundLiveMigrations) {
187
var padId = L.get(i);
189
var t1 = System.currentTimeMillis();
191
if (ensureMigrated(padId, true)) {
192
result = " OK "; // migrated successfully
195
result = " -- "; // no migration needed after all
199
// e just says "migration failed", but presumably
200
// inTransaction() printed a stack trace.
201
// result == "FAIL", do nothing.
203
var t2 = System.currentTimeMillis();
204
_logPadMigration(runnerId, i, totalPads, t2 - t1, result, padId);