We got nominated! Help us out and vote for GitHub as Best Bootstrapped Startup of 2008. (You can vote once a day.) [ hide ]

public
Description: Phusion Passenger (mod_rails)
Homepage: http://www.modrails.com/
Clone URL: git://github.com/FooBarWidget/passenger.git
Click here to lend your support to: passenger and make a donation at www.pledgie.com !
Apply Jochen Tuchbreiter's RailsMaxInstancesPerApp patch. Doesn't pass 
unit tests yet.
Hongli Lai (Phusion) (author)
Thu May 15 13:03:58 -0700 2008
commit  74234781068c109ba523f84c1b48540eb9581499
tree    e2efec51461b5ead61df9cc10a739351a99c96ee
parent  fc830d5ca01c39f615d2d30117aa546fca422b91
...
87
88
89
 
 
 
 
90
91
92
...
115
116
117
 
 
 
 
 
 
 
 
 
118
119
120
...
142
143
144
 
145
146
147
...
155
156
157
158
159
160
161
162
163
164
165
166
 
 
 
 
 
 
 
 
 
 
167
168
169
170
 
 
 
171
172
173
174
175
176
 
 
 
 
 
 
 
 
 
177
178
179
...
183
184
185
186
187
 
188
189
190
191
 
192
193
194
195
196
 
 
 
197
198
199
...
212
213
214
 
 
 
215
216
217
218
 
219
220
 
221
222
223
224
 
 
 
225
226
227
...
283
284
285
 
286
287
288
 
289
290
...
87
88
89
90
91
92
93
94
95
96
...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
155
156
157
158
159
160
161
...
169
170
171
 
 
 
 
 
 
 
 
 
172
173
174
175
176
177
178
179
180
181
182
183
184
 
185
186
187
188
189
 
 
 
 
190
191
192
193
194
195
196
197
198
199
200
201
...
205
206
207
 
 
208
209
210
211
212
213
214
215
216
217
 
218
219
220
221
222
223
...
236
237
238
239
240
241
242
243
 
 
244
245
 
246
247
248
249
250
251
252
253
254
255
256
...
312
313
314
315
316
317
318
319
320
321
0
@@ -87,6 +87,10 @@ information:
0
 - max: integer
0
   The maximum number of AppContainer objects that may exist in 'apps'.
0
 
0
+- max_per_app: integer
0
+ The maximum number of concurrent AppContainer objects a single
0
+ application may spawn.
0
+
0
 - count: integer
0
   The current number of AppContainer objects in 'apps'.
0
   Since 'max' can be set dynamically during the life time of an application
0
@@ -115,6 +119,15 @@ information:
0
      for all keys app_root in restart_times:
0
         apps.has_key(app_root)
0
 
0
+- app_instance_count: map[string => unsigned int]
0
+ Maps an application root to the number of spawned applications.
0
+
0
+ Invariant:
0
+ app_instance_count.keys == apps.keys
0
+ for all keys app_root in app_instance_count:
0
+ app_instance_count[app_root] < count
0
+ (sum of all values in app_instance_count) == count.
0
+
0
 
0
 == Algorithm in pseudo code
0
 
0
@@ -142,6 +155,7 @@ function get(app_root):
0
           list.remove(container.iterator)
0
           if list.empty():
0
             apps.remove(app_root)
0
+ app_instance_count.remove(app_root)
0
           count--
0
           active--
0
 
0
@@ -155,25 +169,33 @@ function spawn_or_use_existing(app_root):
0
   
0
   if (list != nil) and (needs_restart(app_root)):
0
     for all container in list:
0
- if container.sessions == 0:
0
- inactive_apps.remove(container.ia_iterator)
0
- else:
0
- active--
0
- list.remove(container.iterator)
0
- count--
0
- apps.remove(app_root)
0
- list = nil
0
- Tell spawn server to reload code for app_root.
0
+ if container.sessions == 0:
0
+ inactive_apps.remove(container.ia_iterator)
0
+ else:
0
+ active--
0
+ list.remove(container.iterator)
0
+ count--
0
+ apps.remove(app_root)
0
+ app_instance_count.remove(app_root)
0
+ list = nil
0
+ Tell spawn server to reload code for app_root.
0
   
0
   if list != nil:
0
     # There are apps for this app root.
0
- if (list.front.sessions == 0) or (count >= max):
0
+ if (list.front.sessions == 0) or (count >= max) or (
0
+ (max_per_app != 0) and (app_instance_count[app_root] >= max_per_app)
0
+ ):
0
       # There is an inactive app, so we use it.
0
       # -OR-
0
- # All apps are active, and the pool is full. We're not
0
- # allowed to spawn a new app, so we try to connect to
0
- # an existing one. Our connection request will be put
0
- # into that app's connection queue.
0
+ # All apps are active, and the pool is full.
0
+ # -OR-
0
+ # All apps are active and the number of max instances
0
+ # spawned for this rails application has been reached.
0
+ #
0
+ # We're not allowed to spawn a new app, so if there are
0
+ # no inactive apps, we try to connect to an existing one.
0
+ # Our connection request will be put into that app's
0
+ # connection queue.
0
       container = list.front
0
       list.move_to_back(container.iterator)
0
       if container.sessions == 0:
0
@@ -183,17 +205,19 @@ function spawn_or_use_existing(app_root):
0
       # All apps are active, but the pool hasn't reached its
0
       # maximum yet. So we spawn a new app.
0
       container = new AppContainer
0
- # TODO: we should unlock the mutex during spawning,
0
- # and add some kind of timeout check.
0
+ # TODO: we should add some kind of timeout check for spawning.
0
       container.app = spawn(app_root)
0
       container.sessions = 0;
0
       iterator = list.add_to_back(container)
0
       container.iterator = iterator
0
+ app_instance_count[app_root]++
0
       count++
0
       active++
0
   else:
0
     # There are no apps for this app root.
0
- wait until active < max
0
+ wait until
0
+ (active < max) and
0
+ (max_per_app == 0 or app_instance_count[app_root] < max_per_app)
0
     if count == max:
0
       # Here we are in a though situation. There are several
0
       # apps which are inactive, and none of them have
0
@@ -212,16 +236,21 @@ function spawn_or_use_existing(app_root):
0
       if list.empty():
0
         apps.remove(container.app.app_root)
0
         restart_file_times.remove(container.app.app_root)
0
+ app_instance_count.remove(container.app.app_root)
0
+ else:
0
+ app_instance_count[container.app.app_root]--
0
       count--
0
     container = new AppContainer
0
- # TODO: we should unlock the mutex during spawning,
0
- # and add some kind of timeout check.
0
+ # TODO: we should add some kind of timeout check for spawning.
0
     container.app = spawn(app_root)
0
- container.sessions = 0;
0
+ container.sessions = 0
0
     list = apps[app_root]
0
     if list == nil:
0
       list = new list
0
       apps[app_root] = list
0
+ app_instance_count[app_root] = 1
0
+ else:
0
+ app_instance_count[app_root]++
0
     iterator = list.add_to_back(container)
0
     container.iterator = iterator
0
     count++
0
@@ -283,8 +312,10 @@ thread cleaner:
0
         if now - container.last_used > MAX_IDLE_TIME:
0
           app_list.remove(container.iterator)
0
           inactive_apps.remove(iterator for container)
0
+ app_instance_count[app.app_root]--
0
           count--
0
         if app_list.empty():
0
           apps.remove(app.app_root)
0
+ app_instance_count.remove(app.app_root)
0
           restart_file_times.remove(app.app_root)
0
 
...
159
160
161
 
 
 
 
 
 
 
 
 
162
163
164
...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
0
@@ -159,6 +159,15 @@ public:
0
   virtual unsigned int getCount() const = 0;
0
   
0
   /**
0
+ * Set a hard limit on the number of application instances that a single application
0
+ * may spawn in this ApplicationPool. The exact behavior depends on the used algorithm,
0
+ * and is not specified by these API docs.
0
+ *
0
+ * It is allowed to set a limit lower than the current number of spawned applications.
0
+ */
0
+ virtual void setMaxPerApp(unsigned int max) = 0;
0
+
0
+ /**
0
    * Get the process ID of the spawn server that is used.
0
    *
0
    * This method exposes an implementation detail. It is used by unit tests to verify
...
265
266
267
 
 
 
 
 
 
268
269
270
...
265
266
267
268
269
270
271
272
273
274
275
276
0
@@ -265,6 +265,12 @@ private:
0
       return atoi(args[0].c_str());
0
     }
0
     
0
+ virtual void setMaxPerApp(unsigned int max) {
0
+ MessageChannel channel(data->server);
0
+ mutex::scoped_lock l(data->lock);
0
+ channel.write("setMaxPerApp", toString(max).c_str(), NULL);
0
+ }
0
+
0
     virtual pid_t getSpawnServerPid() const {
0
       MessageChannel channel(data->server);
0
       mutex::scoped_lock l(data->lock);
...
256
257
258
 
 
 
 
259
260
261
...
307
308
309
 
 
310
311
312
...
256
257
258
259
260
261
262
263
264
265
...
311
312
313
314
315
316
317
318
0
@@ -256,6 +256,10 @@ private:
0
     channel.write(toString(server.pool.getCount()).c_str(), NULL);
0
   }
0
   
0
+ void processSetMaxPerApp(unsigned int maxPerApp) {
0
+ server.pool.setMaxPerApp(maxPerApp);
0
+ }
0
+
0
   void processGetSpawnServerPid(const vector<string> &args) {
0
     channel.write(toString(server.pool.getSpawnServerPid()).c_str(), NULL);
0
   }
0
@@ -307,6 +311,8 @@ private:
0
           processGetActive(args);
0
         } else if (args[0] == "getCount" && args.size() == 1) {
0
           processGetCount(args);
0
+ } else if (args[0] == "setMaxPerApp" && args.size() == 2) {
0
+ processSetMaxPerApp(atoi(args[1]));
0
         } else if (args[0] == "getSpawnServerPid" && args.size() == 1) {
0
           processGetSpawnServerPid(args);
0
         } else {
...
26
27
28
 
29
30
31
...
86
87
88
 
 
89
90
91
...
104
105
106
 
 
107
108
109
...
123
124
125
 
 
126
127
128
...
212
213
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
216
217
...
301
302
303
 
 
 
 
 
304
305
306
...
26
27
28
29
30
31
32
...
87
88
89
90
91
92
93
94
...
107
108
109
110
111
112
113
114
...
128
129
130
131
132
133
134
135
...
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
...
325
326
327
328
329
330
331
332
333
334
335
0
@@ -26,6 +26,7 @@ using namespace Passenger;
0
 extern "C" module AP_MODULE_DECLARE_DATA passenger_module;
0
 
0
 #define DEFAULT_MAX_POOL_SIZE 20
0
+#define DEFAULT_MAX_INSTANCES_PER_APP 0
0
 #define DEFAULT_POOL_IDLE_TIME 120
0
 
0
 
0
@@ -86,6 +87,8 @@ passenger_config_create_server(apr_pool_t *p, server_rec *s) {
0
   config->root = NULL;
0
   config->maxPoolSize = DEFAULT_MAX_POOL_SIZE;
0
   config->maxPoolSizeSpecified = false;
0
+ config->maxInstancesPerApp = DEFAULT_MAX_INSTANCES_PER_APP;
0
+ config->maxInstancesPerAppSpecified = false;
0
   config->poolIdleTime = DEFAULT_POOL_IDLE_TIME;
0
   config->poolIdleTimeSpecified = false;
0
   config->userSwitching = true;
0
@@ -104,6 +107,8 @@ passenger_config_merge_server(apr_pool_t *p, void *basev, void *addv) {
0
   config->root = (add->root == NULL) ? base->root : add->root;
0
   config->maxPoolSize = (add->maxPoolSizeSpecified) ? base->maxPoolSize : add->maxPoolSize;
0
   config->maxPoolSizeSpecified = base->maxPoolSizeSpecified || add->maxPoolSizeSpecified;
0
+ config->maxInstancesPerApp = (add->maxInstancesPerAppSpecified) ? base->maxInstancesPerApp : add->maxInstancesPerApp;
0
+ config->maxInstancesPerAppSpecified = base->maxInstancesPerAppSpecified || add->maxInstancesPerAppSpecified;
0
   config->poolIdleTime = (add->poolIdleTime) ? base->poolIdleTime : add->poolIdleTime;
0
   config->poolIdleTimeSpecified = base->poolIdleTimeSpecified || add->poolIdleTimeSpecified;
0
   config->userSwitching = (add->userSwitchingSpecified) ? add->userSwitching : base->userSwitching;
0
@@ -123,6 +128,8 @@ passenger_config_merge_all_servers(apr_pool_t *pool, server_rec *main_server) {
0
     final->root = (final->root != NULL) ? final->root : config->root;
0
     final->maxPoolSize = (final->maxPoolSizeSpecified) ? final->maxPoolSize : config->maxPoolSize;
0
     final->maxPoolSizeSpecified = final->maxPoolSizeSpecified || config->maxPoolSizeSpecified;
0
+ final->maxInstancesPerApp = (final->maxInstancesPerAppSpecified) ? final->maxInstancesPerApp : config->maxInstancesPerApp;
0
+ final->maxInstancesPerAppSpecified = final->maxInstancesPerAppSpecified || config->maxInstancesPerAppSpecified;
0
     final->poolIdleTime = (final->poolIdleTimeSpecified) ? final->poolIdleTime : config->poolIdleTime;
0
     final->poolIdleTimeSpecified = final->poolIdleTimeSpecified || config->poolIdleTimeSpecified;
0
     final->userSwitching = (config->userSwitchingSpecified) ? config->userSwitching : final->userSwitching;
0
@@ -212,6 +219,23 @@ cmd_rails_max_pool_size(cmd_parms *cmd, void *pcfg, const char *arg) {
0
 }
0
 
0
 static const char *
0
+cmd_rails_max_instances_per_app(cmd_parms *cmd, void *pcfg, const char *arg) {
0
+ ServerConfig *config = (ServerConfig *) ap_get_module_config(
0
+ cmd->server->module_config, &passenger_module);
0
+ char *end;
0
+ long int result;
0
+
0
+ result = strtol(arg, &end, 10);
0
+ if (*end != '\0') {
0
+ return "Invalid number specified for RailsMaxInstancesPerApp.";
0
+ } else {
0
+ config->maxInstancesPerApp = (unsigned int) result;
0
+ config->maxInstancesPerAppSpecified = true;
0
+ return NULL;
0
+ }
0
+}
0
+
0
+static const char *
0
 cmd_rails_pool_idle_time(cmd_parms *cmd, void *pcfg, const char *arg) {
0
   ServerConfig *config = (ServerConfig *) ap_get_module_config(
0
     cmd->server->module_config, &passenger_module);
0
@@ -301,6 +325,11 @@ const command_rec passenger_commands[] = {
0
     NULL,
0
     RSRC_CONF,
0
     "The maximum number of simultaneously alive Rails application instances."),
0
+ AP_INIT_TAKE1("RailsMaxInstancesPerApp",
0
+ (Take1Func) cmd_rails_max_instances_per_app,
0
+ NULL,
0
+ RSRC_CONF,
0
+ "The maximum number of simultaneously alive Rails application instances a single application may occupy."),
0
   AP_INIT_TAKE1("RailsPoolIdleTime",
0
     (Take1Func) cmd_rails_pool_idle_time,
0
     NULL,
...
76
77
78
 
 
 
 
 
 
 
 
79
80
81
...
76
77
78
79
80
81
82
83
84
85
86
87
88
89
0
@@ -76,6 +76,14 @@
0
        * this server config. */
0
       bool maxPoolSizeSpecified;
0
       
0
+ /** The maximum number of simultaneously alive Rails application
0
+ * that a single Rails application may occupy. */
0
+ unsigned int maxInstancesPerApp;
0
+
0
+ /** Whether the maxInstancesPerApp option was explicitly specified in
0
+ * this server config. */
0
+ bool maxInstancesPerAppSpecified;
0
+
0
       /** The maximum number of seconds that a Rails application may be
0
        * idle before it gets terminated. */
0
       unsigned int poolIdleTime;
...
364
365
366
 
367
368
369
...
364
365
366
367
368
369
370
0
@@ -364,6 +364,7 @@ public:
0
       applicationPool = applicationPoolServer->connect();
0
       applicationPoolServer->detach();
0
       applicationPool->setMax(config->maxPoolSize);
0
+ applicationPool->setMaxPerApp(config->maxInstancesPerApp);
0
       applicationPool->setMaxIdleTime(config->poolIdleTime);
0
     } catch (const exception &e) {
0
       fprintf(stderr, "*** Cannot initialize Passenger: %s\n", e.what());
...
90
91
92
 
93
94
95
...
116
117
118
 
119
120
 
121
122
123
...
179
180
181
 
182
183
 
184
185
186
...
247
248
249
 
 
250
251
252
253
 
254
255
256
...
300
301
302
 
303
304
305
...
307
308
309
310
 
 
311
312
313
...
326
327
328
 
329
330
331
332
333
334
 
 
 
 
335
336
337
...
342
343
344
 
 
 
345
346
347
...
353
354
355
 
356
357
 
358
359
360
...
420
421
422
 
423
424
 
 
425
426
427
428
429
430
 
431
432
433
...
491
492
493
 
494
495
496
...
505
506
507
 
508
509
510
...
529
530
531
 
 
 
 
 
 
532
533
534
...
90
91
92
93
94
95
96
...
117
118
119
120
121
122
123
124
125
126
...
182
183
184
185
186
187
188
189
190
191
...
252
253
254
255
256
257
258
259
260
261
262
263
264
...
308
309
310
311
312
313
314
...
316
317
318
 
319
320
321
322
323
...
336
337
338
339
340
341
342
343
344
 
345
346
347
348
349
350
351
...
356
357
358
359
360
361
362
363
364
...
370
371
372
373
374
375
376
377
378
379
...
439
440
441
442
443
 
444
445
446
447
448
449
450
451
452
453
454
455
...
513
514
515
516
517
518
519
...
528
529
530
531
532
533
534
...
553
554
555
556
557
558
559
560
561
562
563
564
0
@@ -90,6 +90,7 @@ class StandardApplicationPool: public ApplicationPool {
0
 private:
0
   static const int DEFAULT_MAX_IDLE_TIME = 120;
0
   static const int DEFAULT_MAX_POOL_SIZE = 20;
0
+ static const int DEFAULT_MAX_INSTANCES_PER_APP = 0;
0
   static const int CLEANER_THREAD_STACK_SIZE = 1024 * 128;
0
 
0
   friend class ApplicationPoolServer;
0
@@ -116,8 +117,10 @@ private:
0
     unsigned int max;
0
     unsigned int count;
0
     unsigned int active;
0
+ unsigned int maxPerApp;
0
     AppContainerList inactiveApps;
0
     map<string, time_t> restartFileTimes;
0
+ map<string, unsigned int> appInstanceCount;
0
   };
0
   
0
   typedef shared_ptr<SharedData> SharedDataPtr;
0
@@ -179,8 +182,10 @@ private:
0
   unsigned int &max;
0
   unsigned int &count;
0
   unsigned int &active;
0
+ unsigned int &maxPerApp;
0
   AppContainerList &inactiveApps;
0
   map<string, time_t> &restartFileTimes;
0
+ map<string, unsigned int> &appInstanceCount;
0
   
0
   bool needsRestart(const string &appRoot) {
0
     string restartFile(appRoot);
0
@@ -247,10 +252,13 @@ private:
0
           inactiveApps.erase(it);
0
           it = prev;
0
           
0
+ appInstanceCount[app->getAppRoot()]--;
0
+
0
           count--;
0
         }
0
         if (appList->empty()) {
0
           apps.erase(app->getAppRoot());
0
+ appInstanceCount.erase(app->getAppRoot());
0
           data->restartFileTimes.erase(app->getAppRoot());
0
         }
0
       }
0
@@ -300,6 +308,7 @@ private:
0
           count--;
0
         }
0
         apps.erase(appRoot);
0
+ appInstanceCount.erase(appRoot);
0
         spawnManager.reload(appRoot);
0
         it = apps.end();
0
       }
0
@@ -307,7 +316,8 @@ private:
0
       if (it != apps.end()) {
0
         list = it->second.get();
0
     
0
- if (list->front()->sessions == 0 || count >= max) {
0
+ if (list->front()->sessions == 0 || count >= max
0
+ || ( maxPerApp != 0 && appInstanceCount[appRoot] >= maxPerApp )) {
0
           container = list->front();
0
           list->pop_front();
0
           list->push_back(container);
0
@@ -326,12 +336,16 @@ private:
0
           list->push_back(container);
0
           container->iterator = list->end();
0
           container->iterator--;
0
+ appInstanceCount[appRoot]++;
0
           count++;
0
           active++;
0
           activeOrMaxChanged.notify_all();
0
         }
0
       } else {
0
- while (active >= max) {
0
+ while (!(
0
+ active < max &&
0
+ (maxPerApp == 0 || appInstanceCount[appRoot] < maxPerApp)
0
+ )) {
0
           activeOrMaxChanged.wait(l);
0
         }
0
         if (count == max) {
0
@@ -342,6 +356,9 @@ private:
0
           if (list->empty()) {
0
             apps.erase(container->app->getAppRoot());
0
             restartFileTimes.erase(container->app->getAppRoot());
0
+ appInstanceCount.erase(container->app->getAppRoot());
0
+ } else {
0
+ appInstanceCount[container->app->getAppRoot()]--;
0
           }
0
           count--;
0
         }
0
@@ -353,8 +370,10 @@ private:
0
         if (it == apps.end()) {
0
           list = new AppContainerList();
0
           apps[appRoot] = ptr(list);
0
+ appInstanceCount[appRoot] = 1;
0
         } else {
0
           list = it->second.get();
0
+ appInstanceCount[appRoot]++;
0
         }
0
         list->push_back(container);
0
         container->iterator = list->end();
0
@@ -420,14 +439,17 @@ public:
0
     max(data->max),
0
     count(data->count),
0
     active(data->active),
0
+ maxPerApp(data->maxPerApp),
0
     inactiveApps(data->inactiveApps),
0
- restartFileTimes(data->restartFileTimes)
0
+ restartFileTimes(data->restartFileTimes),
0
+ appInstanceCount(data->appInstanceCount)
0
   {
0
     detached = false;
0
     done = false;
0
     max = DEFAULT_MAX_POOL_SIZE;
0
     count = 0;
0
     active = 0;
0
+ maxPerApp = DEFAULT_MAX_INSTANCES_PER_APP;
0
     maxIdleTime = DEFAULT_MAX_IDLE_TIME;
0
     cleanerThread = new thread(
0
       bind(&StandardApplicationPool::cleanerThreadMainLoop, this),
0
@@ -491,6 +513,7 @@ public:
0
           if (list.empty()) {
0
             apps.erase(appRoot);
0
           }
0
+ appInstanceCount.erase(appRoot);
0
           count--;
0
           active--;
0
         }
0
@@ -505,6 +528,7 @@ public:
0
     apps.clear();
0
     inactiveApps.clear();
0
     restartFileTimes.clear();
0
+ appInstanceCount.clear();
0
     count = 0;
0
     active = 0;
0
   }
0
@@ -529,6 +553,12 @@ public:
0
     return count;
0
   }
0
   
0
+ virtual void setMaxPerApp(unsigned int maxPerApp) {
0
+ mutex::scoped_lock l(lock);
0
+ this->maxPerApp = maxPerApp;
0
+ activeOrMaxChanged.notify_all();
0
+ }
0
+
0
   virtual pid_t getSpawnServerPid() const {
0
     return spawnManager.getServerPid();
0
   }

Comments

    No one has commented yet.