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 !
Implement the 'PassengerMaxRequests' configuration option.
Hongli Lai (Phusion) (author)
Sat Aug 02 09:57:57 -0700 2008
commit  8eac99ab46ac8f6e6e395efb4801c71350e11289
tree    4b42af9d4b74692caf245503841706c63c43781d
parent  f0dd5ff86d050a06476d5dc0a9b8869ab9390967
...
5
6
7
8
 
 
9
10
11
12
13
14
15
16
...
61
62
63
 
 
 
 
 
64
65
66
...
85
86
87
 
 
 
 
 
 
 
 
 
 
88
89
90
...
168
169
170
171
 
 
 
 
 
172
173
174
175
176
177
178
 
179
180
181
...
199
200
201
202
 
203
204
205
...
284
285
286
 
287
288
289
...
301
302
303
304
305
 
306
307
308
309
 
 
 
 
 
 
 
 
 
310
 
 
 
 
 
 
 
 
311
312
313
...
5
6
7
 
8
9
10
11
12
 
 
13
14
15
...
60
61
62
63
64
65
66
67
68
69
70
...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
182
183
184
 
185
186
187
188
189
190
191
192
193
194
195
 
196
197
198
199
...
217
218
219
 
220
221
222
223
...
302
303
304
305
306
307
308
...
320
321
322
 
 
323
324
 
 
 
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
0
@@ -5,12 +5,11 @@
0
 
0
 For efficiency reasons, Passenger keeps a pool spawned Rails/Ruby applications.
0
 Please read the C++ API documentation for the ApplicationPool class for a full
0
-introduction. This document describes an algorithm for managing the pool.
0
+introduction. This document describes an algorithm for managing the pool, in a
0
+high-level way.
0
 
0
 The algorithm should strive to keep spawning to a minimum.
0
 
0
-TODO: check whether the algorithm has thrashing behavior.
0
-
0
 
0
 == Definitions
0
 
0
@@ -61,6 +60,11 @@ explicitly define some special types:
0
               containers[i + 1].app is active
0
 
0
   * size (unsigned integer): The number of items in _instances_.
0
+
0
+ * max_requests (unsigned integer): The maximum number of requests that each
0
+ application instance in this domain may process. After having processed this
0
+ many requests, the application instance will be shut down.
0
+ A value of 0 indicates that there is no maximum.
0
 
0
 - AppContainer
0
   A compound type (class) which contains an application instance, as well as
0
@@ -85,6 +89,16 @@ explicitly define some special types:
0
     inactive_apps. This iterator is only valid if this AppContainer really is
0
     in that list.
0
 
0
+- SpawnOptions
0
+ A structure containing additional information, used by the application instance
0
+ spawning process.
0
+
0
+ A SpawnOptions has the following members:
0
+ * max_requests (unsigned integer) - The maximum number of requests that the
0
+ application instance may process. After having processed this many requests,
0
+ the application instance will be shut down. A value of 0 indicates that there
0
+ is no maximum.
0
+
0
 === Special functions
0
 
0
 - spawn(app_root)
0
@@ -168,14 +182,18 @@ Here's an UML diagram in ASCII art:
0
 # Thread-safetiness notes:
0
 # - All wait commands are to unlock the lock during waiting.
0
 
0
-function get(app_root):
0
+# Connect to an existing application instance or to a newly spawned application instance.
0
+# 'app_root' specifies the application root folder of the application. 'options' is an
0
+# object of type 'SpawnOptions', which contains additional information which may be
0
+# relevant for spawning.
0
+function get(app_root, options):
0
   MAX_ATTEMPTS = 10
0
   attempt = 0
0
   time_limit = now() + 5 seconds
0
   lock.synchronize:
0
     while (true):
0
       attempt++
0
- container, domain = spawn_or_use_existing(app_root)
0
+ container, domain = spawn_or_use_existing(app_root, options)
0
       container.last_used = current_time()
0
       container.sessions++
0
       try:
0
@@ -199,7 +217,7 @@ function get(app_root):
0
 # Returns a pair of [AppContainer, Domain] that matches the given application
0
 # root. If no such AppContainer exists, then it is created and a new
0
 # application instance is spawned. All exceptions that occur are propagated.
0
-function spawn_or_use_existing(app_root):
0
+function spawn_or_use_existing(app_root, options):
0
   domain = domains[app_root]
0
   
0
   if (domain != nil) and (needs_restart(app_root)):
0
@@ -284,6 +302,7 @@ function spawn_or_use_existing(app_root):
0
     if domain == nil:
0
       domain = new Domain
0
       domain.size = 1
0
+ domain.max_requests = options.max_requests
0
       domains[app_root] = domain
0
     else:
0
       domain.size++
0
@@ -301,13 +320,26 @@ function session_has_been_closed(container):
0
   lock.synchronize:
0
     domain = domains[container.app.app_root]
0
     if domain != nil:
0
- container.last_used = current_time()
0
- container.sessions--
0
+ instances = domain.instances
0
       container.processed++
0
- if container.sessions == 0:
0
- domain.instances.move_to_front(container.iterator)
0
- container.ia_iterator = inactive_apps.add_to_back(container.app)
0
+
0
+ if (domain.max_requests) > 0 and (container.processed >= domain.max_requests):
0
+ # The application instance has processed its maximum allowed
0
+ # number of requests, so we shut it down.
0
+ instances.remove(container.iterator)
0
+ domain.size--
0
+ if instances.empty():
0
+ domains.remove(app_root)
0
+ count--
0
         active--
0
+ else:
0
+ container.last_used = current_time()
0
+ container.sessions--
0
+ container.processed++
0
+ if container.sessions == 0:
0
+ instances.move_to_front(container.iterator)
0
+ container.ia_iterator = inactive_apps.add_to_back(container.app)
0
+ active--
0
 
0
 
0
 function needs_restart(app_root):
...
113
114
115
 
116
117
118
...
192
193
194
195
196
197
 
 
 
198
199
 
200
201
202
203
204
205
 
 
 
 
 
206
207
 
 
 
 
 
 
 
 
 
 
 
 
 
208
209
210
...
522
523
524
 
525
526
527
...
113
114
115
116
117
118
119
...
193
194
195
 
 
 
196
197
198
199
 
200
201
 
 
 
 
 
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
536
537
538
539
540
541
542
0
@@ -113,6 +113,7 @@ private:
0
   struct Domain {
0
     AppContainerList instances;
0
     unsigned int size;
0
+ unsigned long maxRequests;
0
   };
0
   
0
   struct AppContainer {
0
@@ -192,19 +193,32 @@ private:
0
       DomainMap::iterator it;
0
       it = data->domains.find(container->app->getAppRoot());
0
       if (it != data->domains.end()) {
0
- AppContainerList *instances = &it->second->instances;
0
- container->lastUsed = time(NULL);
0
- container->sessions--;
0
+ Domain *domain = it->second.get();
0
+ AppContainerList *instances = &domain->instances;
0
+
0
         container->processed++;
0
- if (container->sessions == 0) {
0
+ if (domain->maxRequests > 0 && container->processed >= domain->maxRequests) {
0
           instances->erase(container->iterator);
0
- instances->push_front(container);
0
- container->iterator = instances->begin();
0
- data->inactiveApps.push_back(container);
0
- container->ia_iterator = data->inactiveApps.end();
0
- container->ia_iterator--;
0
+ domain->size--;
0
+ if (instances->empty()) {
0
+ data->domains.erase(container->app->getAppRoot());
0
+ }
0
+ data->count--;
0
           data->active--;
0
           data->activeOrMaxChanged.notify_all();
0
+ } else {
0
+ container->lastUsed = time(NULL);
0
+ container->sessions--;
0
+ if (container->sessions == 0) {
0
+ instances->erase(container->iterator);
0
+ instances->push_front(container);
0
+ container->iterator = instances->begin();
0
+ data->inactiveApps.push_back(container);
0
+ container->ia_iterator = data->inactiveApps.end();
0
+ container->ia_iterator--;
0
+ data->active--;
0
+ data->activeOrMaxChanged.notify_all();
0
+ }
0
         }
0
       }
0
     }
0
@@ -522,6 +536,7 @@ private:
0
         if (it == domains.end()) {
0
           domain = new Domain();
0
           domain->size = 1;
0
+ domain->maxRequests = spawnOptions.maxRequests;
0
           domains[appRoot] = ptr(domain);
0
         } else {
0
           domain = it->second.get();
...
364
365
366
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
368
369
...
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
0
@@ -364,6 +364,39 @@
0
     // TODO: how do we test this?
0
   }
0
   
0
+ TEST_METHOD(18) {
0
+ // Application instance is shutdown after 'maxRequests' requests.
0
+ SpawnOptions options("stub/railsapp");
0
+ int reader;
0
+ pid_t originalPid;
0
+ Application::SessionPtr session;
0
+
0
+ options.maxRequests = 4;
0
+ pool->setMax(1);
0
+ session = pool->get(options);
0
+ originalPid = session->getPid();
0
+ session.reset();
0
+
0
+ for (unsigned int i = 0; i < 4; i++) {
0
+ session = pool->get(options);
0
+ session->sendHeaders(createRequestHeaders());
0
+ session->shutdownWriter();
0
+ reader = session->getStream();
0
+ readAll(reader);
0
+ // Must explicitly call reset() here because we
0
+ // want to close the session right now.
0
+ session.reset();
0
+ // In case of ApplicationPoolServer, we sleep here
0
+ // for a little while to force a context switch to
0
+ // the server, so that the session close event may
0
+ // be processed.
0
+ usleep(100000);
0
+ }
0
+
0
+ session = pool->get(options);
0
+ ensure(session->getPid() != originalPid);
0
+ }
0
+
0
   // TODO: test maxIdleTime == 0
0
 
0
 #endif /* USE_TEMPLATE */
...
2
3
4
 
 
 
 
5
...
2
3
4
5
6
7
8
9
0
@@ -2,4 +2,8 @@ class FooController < ActionController::Base
0
   def new
0
     render :text => 'hello world'
0
   end
0
+
0
+ def pid
0
+ render :text => Process.pid.to_s
0
+ end
0
 end

Comments

    No one has commented yet.