~davewalker/etherpad/ubuntu-unlimited-max-users-and-revisions

« back to all changes in this revision

Viewing changes to etherpad/src/etherpad/pad/pad_migrations.js

  • Committer: James Page
  • Date: 2011-04-13 08:00:43 UTC
  • Revision ID: james.page@canonical.com-20110413080043-eee2nq7y1v7cv2mp
* Refactoring to use native Ubuntu Java libraries. 
* debian/control:
  - use openjdk instead of sun's java
  - update maintainer
* debian/etherpad.init.orig, debian/etherpad.upstart:
  - move the init script out of the way
  - create a basic upstart script
  - note that the open office document conversion daemon was dropped
    from the upstart configuration; if this behavior is desired, please
    create a separate upstart job for it
* debian/rules:
  - just use basic dh_installinit, as it will pick up the new upstart job
* New release
* Changed maintainer to Packaging
* Fixed installation scripts
* Initial Release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * Copyright 2009 Google Inc.
 
3
 * 
 
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
 
7
 * 
 
8
 *      http://www.apache.org/licenses/LICENSE-2.0
 
9
 * 
 
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.
 
15
 */
 
16
 
 
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");
 
28
 
 
29
function onStartup() {
 
30
  if (! appjet.cache.pad_migrations) {
 
31
    appjet.cache.pad_migrations = {};
 
32
  }
 
33
 
 
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();
 
38
  //}
 
39
}
 
40
 
 
41
function initLiveMigration() {
 
42
 
 
43
  if (! appjet.cache.pad_migrations) {
 
44
    appjet.cache.pad_migrations = {};
 
45
  }
 
46
  appjet.cache.pad_migrations.doingAnyLiveMigrations = true;
 
47
  appjet.cache.pad_migrations.doingBackgroundLiveMigrations = true;
 
48
  appjet.cache.pad_migrations.padMap = new ConcurrentHashMap();
 
49
 
 
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});
 
55
  });
 
56
}
 
57
 
 
58
function _padMap() {
 
59
  return appjet.cache.pad_migrations.padMap;
 
60
}
 
61
 
 
62
function _doingItLive() {
 
63
  return !! appjet.cache.pad_migrations.doingAnyLiveMigrations;
 
64
}
 
65
 
 
66
function checkPadStatus(padId) {
 
67
  if (! _doingItLive()) {
 
68
    return "ready";
 
69
  }
 
70
  var info = _padMap().get(padId);
 
71
  if (! info) {
 
72
    return "ready";
 
73
  }
 
74
  else if (info.migrating) {
 
75
    return "migrating";
 
76
  }
 
77
  else {
 
78
    return "oldversion";
 
79
  }
 
80
}
 
81
 
 
82
function ensureMigrated(padId, async) {
 
83
  if (! _doingItLive()) {
 
84
    return false;
 
85
  }
 
86
 
 
87
  var info = _padMap().get(padId);
 
88
  if (! info) {
 
89
    // pad is up-to-date
 
90
    return false;
 
91
  }
 
92
  else if (async && info.migrating) {
 
93
    // pad is already being migrated, don't wait on the lock
 
94
    return false;
 
95
  }
 
96
 
 
97
  return model.doWithPadLock(padId, function() {
 
98
    // inside pad lock...
 
99
    var info = _padMap().get(padId);
 
100
    if (!info) {
 
101
      return false;
 
102
    }
 
103
    // migrate from version 1 to version 2 in a transaction
 
104
    var migrateSucceeded = false;
 
105
    try {
 
106
      info.migrating = true;
 
107
      log.info("Migrating pad "+padId+" from version 1 to version 2...");
 
108
 
 
109
      var success = false;
 
110
      var whichTry = 1;
 
111
      while ((! success) && whichTry <= 3) {
 
112
        success = sqlcommon.inTransaction(function() {
 
113
          try {
 
114
            easysync2migration.migratePad(padId);
 
115
            sqlobj.update("PAD_SQLMETA", {id: padId}, {version: 2});
 
116
            return true;
 
117
          }
 
118
          catch (e if (e.toString().indexOf("try restarting transaction") >= 0)) {
 
119
            whichTry++;
 
120
            return false;
 
121
          }
 
122
        });
 
123
        if (! success) {
 
124
          java.lang.Thread.sleep(Math.floor(Math.random()*200));
 
125
        }
 
126
      }
 
127
      if (! success) {
 
128
        throw new Error("too many retries");
 
129
      }
 
130
 
 
131
      migrateSucceeded = true;
 
132
      log.info("Migrated pad "+padId+".");
 
133
      _padMap().remove(padId);
 
134
    }
 
135
    finally {
 
136
      info.migrating = false;
 
137
      if (! migrateSucceeded) {
 
138
        log.info("Migration failed for pad "+padId+".");
 
139
        throw new Error("Migration failed for pad "+padId+".");
 
140
      }
 
141
    }
 
142
    return true;
 
143
  });
 
144
}
 
145
 
 
146
function numUnmigratedPads() {
 
147
  if (! _doingItLive()) {
 
148
    return 0;
 
149
  }
 
150
 
 
151
  return _padMap().size();
 
152
}
 
153
 
 
154
////////// BACKGROUND MIGRATIONS
 
155
 
 
156
function _logPadMigration(runnerId, padNumber, padTotal, timeMs, fourCharResult, padId) {
 
157
  log.custom("pad_migrations", {
 
158
    runnerId: runnerId,
 
159
    padNumber: Math.round(padNumber+1),
 
160
    padTotal: Math.round(padTotal),
 
161
    timeMs: Math.round(timeMs),
 
162
    fourCharResult: fourCharResult,
 
163
    padId: padId});
 
164
}
 
165
 
 
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)))) {
 
170
      L.remove(i);
 
171
    }
 
172
  }
 
173
  return L;
 
174
}
 
175
 
 
176
function runBackgroundMigration(residue, modulus, runnerId) {
 
177
  var L = _getNeededMigrationsArrayList(function(padId) {
 
178
    return (padId.charCodeAt(0) % modulus) == residue;
 
179
  });
 
180
  Collections.shuffle(L);
 
181
 
 
182
  var totalPads = L.size();
 
183
  for(var i=0;i<totalPads;i++) {
 
184
    if (! appjet.cache.pad_migrations.doingBackgroundLiveMigrations) {
 
185
      break;
 
186
    }
 
187
    var padId = L.get(i);
 
188
    var result = "FAIL";
 
189
    var t1 = System.currentTimeMillis();
 
190
    try {
 
191
      if (ensureMigrated(padId, true)) {
 
192
        result = " OK "; // migrated successfully
 
193
      }
 
194
      else {
 
195
        result = " -- "; // no migration needed after all
 
196
      }
 
197
    }
 
198
    catch (e) {
 
199
      // e just says "migration failed", but presumably
 
200
      // inTransaction() printed a stack trace.
 
201
      // result == "FAIL", do nothing.
 
202
    }
 
203
    var t2 = System.currentTimeMillis();
 
204
    _logPadMigration(runnerId, i, totalPads, t2 - t1, result, padId);
 
205
  }
 
206
}