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! !