Stephen Deasey wrote:

Your single thread helper mechanism above may not work the disk as
hard as multiple threads, but it does mean that the driver thread
doesn't have to block waiting for disk.
it does not necessary have to write to the disk.
You've also moved quota checking etc. into the helper thread.  This
doesn't so much matter if there's just one, but with more than one you
have to remember that a Tcl interp will be allocated for each, and
they'll be just as heavyweight as normal conn threads.

We have a similarly problem with normal downloads, i.e. writing data
to the client, an mp3 file for example, ties up a heavyweight conn
thread.  A generic pool of IO threads might be useful in a number of
places.  Of course, this is just a simulation of AIO to disk, but see
my other email for why AIO is not so easy.
we are using on our production site a background delivery for large files (up to 4 mio requests per day, up to 30 GB/day, up to 1200 concurrent users, oacs) which does not block the connection threads. i did this with some help from zoran and it works like a champ (single spooling thread, using ns_conn channel + libthread with thread::transfer and thread:;send code below). This bgdelivery implementation has already reduced the memory footprint of our server, frees us from slow-download-DOS-"attacks" and yielded no measurable
performance degradation.
AOLserver HEAD has a new filter type, NS_FILTER_PREQUEUE, which gets
called just before the connection is queued for processing by a conn
thread.  The unique thing about this filter type is that the
registered code runs in the context of the driver thread. Unfortunately that's after all the data has been read and spooled, but
we could implement it such that they ran just after all headers have
been received.
interesting
If you want to check quotas by querying a database then then this
doesn't help -- your db query will block the driver thread.  These
filters will have to be coded carefully.  We could also implement
non-blocking db calls, and that's certainly something I'm interested
in, but it doesn't help right now.
true.
However, if the code you want to run is short and non blocking,
prequeue filters we be a good way to run quota checks and such without
having to involve a conn thread at all.
also the singed cookie checking of oacs might be already to expensive for a single thread (no matter whether is happens in the driver or in the model suggested in my last mail).

The more i think about the problem, the general case (expensive permission + quota checking) of uploads should be handled by connection threads. In order to avoid having these sitting more or less idle around, one cal use a spooling thread (or a few of these).
What i have seen, all ajax file upload bars are associating an
Upload-ID with the file upload and query later via GET requests the status of these. This means, that ordinary requests must be able to query the status... This is quite similar to my request monitor part in oacs-head (you can see this in mannheim in action, where it is
publically accessible https://dotlrn.uni-mannheim.de/request-monitor/)

A revised model:
1) request HEAD processing alway in the driver thread
2) for incoming data processing delegation to a connection thread with a new filter type
    (can do db-queries etc)
3) delegation to a spooling thread (where it can be monitored)
4) delegation to a connection thread ala  aolserver.

when end-of-file/error occurs in the spooling thread, it would require a tcl-command
to enqueue the request as connection thread.

Actually, this could be done only when there is a POST/PUT request with a
Upload-ID as a query parameter, such that the programmer has full control
when this happen, there will be no penalty for small forms...

-gustaf

=====================================================
ad_library {
   Routines for background delivery of files

   @author Gustaf Neumann ([EMAIL PROTECTED])
   @creation-date 19 Nov 2005
@cvs-id $Id: background-delivery-procs.tcl,v 1.2 2005/12/30 00:07:23 gustafn Exp $
}

::xotcl::THREAD create bgdelivery {
 set ::delivery_count 0

 proc deliver {ch filename context} {
   set fd [open $filename]
   fconfigure $fd -translation binary
   fconfigure $ch -translation binary
   fcopy $fd $ch -command [list end-delivery $filename $fd $ch]
   set ::running($ch,$filename) $context
   incr ::delivery_count
 }

 proc end-delivery {filename fd ch bytes args} {
#ns_log notice "--- end of delivery of $filename, $bytes bytes written $args" if {[catch {close $ch} e]} {ns_log notice "bgdelivery, closing channel for $filename, error: $e"} if {[catch {close $fd} e]} {ns_log notice "bgdelivery, closing file $filename, error: $e"}
   unset ::running($ch,$filename)
 }
} -persistent 1

bgdelivery ad_forward running {
Interface to the background delivery thread to query the currently running deliveries. @return list of key value pairs of all currently running background processes
} %self do array get running


bgdelivery ad_forward nr_running {
Interface to the background delivery thread to query the number of currently running deliveries.
 @return number of currently running background deliveries
} %self do array size running

if {[ns_info name] eq "NaviServer"} {
 bgdelivery forward write_headers ns_headers
} else {
 bgdelivery forward write_headers ns_headers DUMMY
}

bgdelivery ad_proc returnfile {statuscode mime_type filename} {
Deliver the given file to the requestor in the background. This proc uses the background delivery thread to send the file in an event-driven manner without blocking a request thread. This is especially important when large files are
 requested over slow (e.g. dial-ip) connections.
} {
 set size [file size $filename]
 if {[my write_headers $statuscode $mime_type $size]} {
   set ch [ns_conn channel]
   thread::transfer [my get_tid] $ch
   throttle get_context
   my do -async deliver $ch $filename \
   [list [throttle set requestor],[throttle set url] [ns_conn start]]
   ns_conn contentsentlength $size       ;# maybe overly optimistic
 }
}





Reply via email to