I've written a small stats package for Comanche - it keeps track of
processes and requests. It displays the number of currently running
Comanche processes, the maximum simulataneous count (spikes), and
total process count. Also, it displays number of "good" requests and
nil requests. Nil requests happen when no data is read on the socket
and thus no request is created.

Brief testing of Win2K vs Win NT 4.0 SP6 shows that NT had no nil
requests (one user, both local and remote), whereas Win2K doesn't have
nil requests when accessing from a remote machine, but generates
roughly 25% nil requests when on a local machine -- even on a single
Swiki page (20 good requests, 6 nil requests).

The KomStats change set is enclosed -- if you'd like to try and see it
for yourself. I also open ProcessBrowser (and set it to automatically
update), to be able to inspect contexts and/or objects that are
delayed beyond reason (these usually turn out to be nil requests).

Please let me know if you have a suggestion for what other stats to
keep, which could shed a light on what goes wrong (and is the root of
problems) in Comanche/Swiki.

Bolot

'From Squeak3.1alpha of 7 March 2001 [latest update: #4019] on 16 May 2001 at 7:14:06 
pm'!
"Change Set:         KomStats
Date:                  16 May 2001
Author:          
           Bolot Kerimbaev

Big goal: make Comanche super-stable. Sub-goal: identify 
problems that bring it down on some platforms (Win2K, MacOS).

This change set 
includes:
- gather statistics in HttpAdaptor and ComancheNetService
- HttpAdaptor 
stats:
-- requestGoodCount, requestNilCount -- there were a lot of nil requests on 
Win2K box, hypothesis is that they hang around in Processes (waiting to terminate on 
Socket timeout), and eat up resources
- ComancheNetRequest stats:
-- processCount, 
processCountMax, processCountTotal -- we want to see burstiness and overall lifetime. 
Note that processCountTotal = requestGoodCount + requestNilCount

A few changes were 
made to improve performance, but they could be problematic:
- SocketStream>>close 
calls closeAndDestroy: with 5 seconds, rather than 30, so that closing happens faster 
-- but it could cause not all data to be transmitted to clients on slow connections, 
if flush didn't work well
- ConnectionHandler>>pvtNewListenLoop rearranges some code, 
in accordance with ConnectionQueue's equivalent code, plus reduces Delay to 10 ms, 
instead of 100 -- this should be fine, but the two methods in comparison are markedly 
different"!

ComancheService subclass: #ComancheNetService
        
instanceVariableNames: 'comancheServer protocolAdaptor module '
        
classVariableNames: 'Stats '
   poolDictionaries: ''
   category: 'Kom-kernel'!
NetworkProtocolAdaptor subclass: #HttpAdaptor
  instanceVariableNames: 'request 
response startTime '
   classVariableNames: 'Stats '
   poolDictionaries: ''
   
category: 'Kom-protocol HTTP'!

!ComancheNetService methodsFor: 'conversation'!
serve: 
aSocket
        "Provided for TCP/IP conversations.  Works in conjunction with 
        a ComancheServer.  Services that do not serve TCP/IP connections
        do not need to implement this method."

       self class processCountIncrease.
       self class displayStats.
        self protocolAdaptor 
                readAndWriteTo: (SocketStream on: aSocket)
                target: self.
  self class processCountDecrease.
       self class 
displayStats.! !


!ComancheNetService class methodsFor: 'instance creation'!
named: 
aString onPort: portNumber priority: priorityNum protocolAdaptor: aPA 
limitConnectionsTo: aNumber
      | instance |
   instance _ (super named: aString)
   
           initializeOnPort: portNumber
           priority: priorityNum
          
protocolAdaptor: aPA
           serverClass: LTDComancheServer.
        instance 
comancheServer limitConnectionsTo: aNumber.
   ^instance! !

!ComancheNetService class 
methodsFor: 'stats'!
displayStats
      self stats asString displayAt: 10@100! !

!ComancheNetService class methodsFor: 'stats'!
processCount
  ^self stats at: 
#processCount ifAbsent: [0]! !

!ComancheNetService class methodsFor: 'stats'!
processCountDecrease
    self stats at: #processCount ifAbsentPut: [0].
 self stats 
at: #processCount modify: [:n | (n - 1) max: 0]! !

!ComancheNetService class 
methodsFor: 'stats'!
processCountIncrease
     self stats at: #processCount put: self 
processCount + 1.
       self processCountMax: self processCount.
       self 
processCountTotalIncrease! !

!ComancheNetService class methodsFor: 'stats'!
processCountMax
      ^self stats at: #processCountMax ifAbsent: [0]! !

!ComancheNetService class methodsFor: 'stats'!
processCountMax: aNumber
     aNumber > 
self processCountMax
         ifTrue: [self stats at: #processCountMax put: aNumber]! 
!

!ComancheNetService class methodsFor: 'stats'!
processCountTotalIncrease
    self 
stats at: #processCountTotal ifAbsentPut: [0].
    self stats at: #processCountTotal 
modify: [:n | n + 1]! !

!ComancheNetService class methodsFor: 'stats'!
resetStats
   
self stats keysAndValuesDo: [:k :v |
           v isNumber ifTrue: [self stats at: k 
put: 0]].
 self displayStats! !

!ComancheNetService class methodsFor: 'stats'!
stats
 
    ^Stats ifNil: [Stats _ Dictionary new]! !


!ConnectionHandler methodsFor: 
'private'!
pvtNewListenLoop
        | socket listener |

        listener _ self newListener.
        self socketsToDestroy add: listener.    

        "If the listener is not valid then the we cannot use the
        BSD style accept() mechanism."
        listener isValid ifFalse: [^self pvtOldListenLoop].
        [ true ] whileTrue: [
                socket _ listener waitForAcceptUntil: (Socket deadlineSecs: 10).
                (socket notNil and: [ socket isConnected ])
                        ifTrue: [
                                handlerBlock value: socket.
                                socket _ nil]
                        ifFalse: [
                                socket notNil ifTrue: [socket destroy].
                                listener isValid
                                        ifFalse: [listener destroy.
                                                (Delay forMilliseconds: 10) wait.
                                                listener _ self newListener].
                        ].
        ]! !


!HttpAdaptor methodsFor: 'processing'!
beginConversation
        startTime _ Time millisecondClockValue.
        [
                [(self pvtReadRequest) ifNotNil:
                        [self class requestGoodCountIncrease.
                  self 
class displayStats.
                       request isHeaderRequest ifTrue: ['head' 
displayAt: 10@140].
                    self pvtGetResponseAndDo:
                                [self pvtWriteResponse.
                                self pvtDestroyGently.
                                "self pvtWriteLogAndDestroy"]
                        ]
                      ifNil: [self class 
requestNilCountIncrease.
                            self class displayStats].
                self isPersistentConnection and: [Preferences keepAliveConnections]] 
whileTrue.
                self pvtDestroy
        ] on: Error do: [ :ex |
                (Preferences standaloneServer) ifTrue:
                        [self pvtDestroy.
                        self pvtHandleError: ex]
                ifFalse: "re-raise the exception"
                        [ex signal].
        ]! !


!HttpAdaptor class methodsFor: 'stats'!
displayStats
    self stats 
asString displayAt: 10@120! !

!HttpAdaptor class methodsFor: 'stats'!
requestGoodCount
     ^self stats at: #requestGoodCount ifAbsent: [0]! !

!HttpAdaptor 
class methodsFor: 'stats'!
requestGoodCountIncrease
   self stats at: 
#requestGoodCount put: self requestGoodCount + 1! !

!HttpAdaptor class methodsFor: 
'stats'!
requestNilCount
    ^self stats at: #requestNilCount ifAbsent: [0]! !

!HttpAdaptor class methodsFor: 'stats'!
requestNilCountIncrease
     self stats at: 
#requestNilCount put: self requestNilCount + 1! !

!HttpAdaptor class methodsFor: 
'stats'!
resetStats
   self stats keysAndValuesDo: [:k :v |
           v isNumber 
ifTrue: [self stats at: k put: 0]].
 self displayStats! !

!HttpAdaptor class 
methodsFor: 'stats'!
stats
    ^Stats ifNil: [Stats _ Dictionary new]! !


!SocketStream methodsFor: 'initialize-release'!
close
        self flush.
        self socket closeAndDestroy: 5! !

Reply via email to