http-cache.tcl
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:37k
源码类别:

通讯编程

开发平台:

Visual C++

  1. # Copyright (c) Xerox Corporation 1998. All rights reserved.
  2. #
  3. # This program is free software; you can redistribute it and/or modify it
  4. # under the terms of the GNU General Public License as published by the
  5. # Free Software Foundation; either version 2 of the License, or (at your
  6. # option) any later version.
  7. # This program is distributed in the hope that it will be useful, but
  8. # WITHOUT ANY WARRANTY; without even the implied warranty of
  9. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10. # General Public License for more details.
  11. # You should have received a copy of the GNU General Public License along
  12. # with this program; if not, write to the Free Software Foundation, Inc.,
  13. # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  14. # Linking this file statically or dynamically with other modules is making
  15. # a combined work based on this file.  Thus, the terms and conditions of
  16. # the GNU General Public License cover the whole combination.
  17. # In addition, as a special exception, the copyright holders of this file
  18. # give you permission to combine this file with free software programs or
  19. # libraries that are released under the GNU LGPL and with code included in
  20. # the standard release of ns-2 under the Apache 2.0 license or under
  21. # otherwise-compatible licenses with advertising requirements (or modified
  22. # versions of such code, with unchanged license).  You may copy and
  23. # distribute such a system following the terms of the GNU GPL for this
  24. # file and the licenses of the other code concerned, provided that you
  25. # include the source code of that other code when and as the GNU GPL
  26. # requires distribution of source code.
  27. # Note that people who make modified versions of this file are not
  28. # obligated to grant this special exception for their modified versions;
  29. # it is their choice whether to do so.  The GNU General Public License
  30. # gives permission to release a modified version without this exception;
  31. # this exception also makes it possible to release a modified version
  32. # which carries forward this exception.
  33. #
  34. # Implementation of web cache
  35. #
  36. # $Header: /cvsroot/nsnam/ns-2/tcl/webcache/http-cache.tcl,v 1.14 2005/08/26 05:05:30 tomh Exp $
  37. Http/Cache instproc init args {
  38. eval $self next $args
  39. $self instvar node_ stat_
  40. $node_ color "yellow" ;# no page
  41. array set stat_ [list hit-num 0 barrival 0 ims-num 0]
  42. }
  43. Http instproc set-cachesize { size } {
  44. $self instvar pool_
  45. $pool_ set max_size_ $size
  46. }
  47. Http instproc get-cachesize {} {
  48. $self instvar pool_
  49. return [$pool_ set max_size_]
  50. }
  51. # It's the user's responsibility to connect clients to caches, and caches to
  52. # servers. Note that a cache may connect to many other caches and servers, 
  53. # but it has only one parent cache
  54. Http/Cache instproc connect { server } {
  55. $self next $server
  56. }
  57. Http/Cache instproc disconnect { http } {
  58. $self instvar slist_ clist_
  59. if [$http info class Http/Cache] {
  60. error "Cannot disconnect a cache from another cache"
  61. }
  62. if {[lsearch $slist_ $http] >= 0} {
  63. $self disconnect-server $http
  64. } else {
  65. $self disconnect-client $http
  66. }
  67. }
  68. # XXX Should add pending_ handling into disconnect
  69. Http/Cache instproc disconnect-server { server } {
  70. $self instvar ns_ slist_ node_
  71. set pos [lsearch $slist_ $server]
  72. if {$pos >= 0} {
  73. lreplace $slist_ $pos $pos
  74. } else { 
  75. error "Http::disconnect: not connected to $server"
  76. }
  77. set tcp [[$self get-cnc $server] agent]
  78. $self cmd disconnect $server
  79. $server disconnect $self
  80. $tcp proc done {} "$ns_ detach-agent $node_ $tcp; delete $tcp"
  81. $tcp close
  82. #puts "cache [$self id] disconnect from server [$server id]"
  83. # Clear all states related to the server. 
  84. # XXX Assume the server isn't a cache!
  85. $self instvar pending_
  86. foreach p [array names pending_] {
  87. if {$server == [lindex [split $p :] 0]} {
  88. unset pending_($p)
  89. }
  90. }
  91. }
  92. # XXX Should clean up client request states
  93. Http/Cache instproc disconnect-client { client } {
  94. $self instvar ns_ clist_ node_
  95. set pos [lsearch $clist_ $client]
  96. if {$pos >= 0} {
  97. lreplace $clist_ $pos $pos
  98. } else { 
  99. error "Http/Cache::disconnect: not connected to $server"
  100. }
  101. set tcp [[$self get-cnc $client] agent]
  102. $self cmd disconnect $client
  103. $tcp proc done {} "$ns_ detach-agent $node_ $tcp; delete $tcp"
  104. $tcp close
  105. #puts "cache [$self id] disconnect from client [$client id]"
  106. # Clear all pending requests associated with the client
  107. $self instvar creq_
  108. foreach p [array names creq_] {
  109. set res {}
  110. for {set i 0} {$i < [llength $creq_($p)]} {incr i} {
  111. set clt [lindex $creq_($p) $i]
  112. if {$client != [lindex [split clt /] 0]} {
  113. lappend res $clt
  114. }
  115. }
  116. if {[llength $res] == 0} {
  117. unset creq_($p)
  118. } else {
  119. set creq_($p) $res
  120. }
  121. }
  122. }
  123. # Use this function to construct a cache hierarchy
  124. Http/Cache instproc set-parent { server } {
  125. $self instvar parent_
  126. set parent_ $server
  127. }
  128. # Copied from Http/Server
  129. # Let the client side to do the actual connection ($ns connect)
  130. Http/Cache instproc alloc-connection { client fid } {
  131. Http instvar TRANSPORT_
  132. $self instvar ns_ clist_ node_ id_ fid_
  133. lappend clist_ $client
  134. set snk [new Agent/TCP/$TRANSPORT_]
  135. $snk set fid_ $fid
  136. $ns_ attach-agent $node_ $snk
  137. $snk listen
  138. set wrapper [new Application/TcpApp $snk]
  139. $self cmd connect $client $wrapper
  140. #puts "Cache $id_ connected to client [$client id]"
  141. return $wrapper
  142. }
  143. # Parameters different from Http/Client::send-request. This one needs 
  144. # size of the request because it may need to forward a client's request to 
  145. # a server.
  146. Http/Cache instproc send-request { server type pageid size args } {
  147. $self instvar ns_ pending_ ;# pending requests, includes those 
  148. ;# from itself
  149. # Don't bother sending a request to a not-connected server
  150. if ![$self is-connected $server] {
  151. return
  152. }
  153. set pending_($pageid) [$ns_ now]
  154. $self send $server $size 
  155.     "$server get-request $self $type $pageid size $size [join $args]"
  156. }
  157. # By constructing page id as tuple (server name, page id) we build in 
  158. # support for multiple web servers
  159. Http/Cache instproc get-request { cl type pageid args } {
  160. $self instvar slist_ clist_ ns_ id_ pending_ stat_
  161. incr stat_(hit-num)
  162. array set data $args
  163. if ![info exists data(size)] {
  164. error "Http/Cache $id_: client [$cl id] must include request size in its request"
  165. }
  166. if [$self exist-page $pageid] {
  167. $self cache-hit $cl $type $pageid 
  168. } else {
  169. $self cache-miss $cl $type $pageid
  170. }
  171. }
  172. # Cache miss, get it from the server
  173. Http/Cache instproc cache-miss { cl type pageid } {
  174. $self instvar parent_ pending_ 
  175. creq_ ;# pending client requests
  176. # Another client requests for the page
  177. lappend creq_($pageid) $cl/$type
  178. # XXX If there's a previous requests going on we won't send another
  179. # request for the same page.
  180. if [info exists pending_($pageid)] {
  181. return
  182. }
  183. # Page not found, contact parent and get the page. If parent_ == 0,
  184. # which means this is a root cache, directly contact the server
  185. set server [lindex [split $pageid :] 0]
  186. if [info exists parent_] {
  187. set server $parent_
  188. }
  189. set size [$self get-reqsize]
  190. $self evTrace E MISS p $pageid c [$cl id] s [$server id] z $size
  191. $self send-request $server $type $pageid $size
  192. }
  193. # Check if page $pageid is consistent. If not, refetch the page from server.
  194. Http/Cache instproc is-consistent { cl type pageid } {
  195. return 1
  196. }
  197. Http/Cache instproc refetch-pending { cl type pageid } {
  198. return 0
  199. }
  200. Http/Cache instproc refetch args {
  201. # Do nothing
  202. }
  203. Http/Cache instproc cache-hit { cl type pageid } {
  204. # page found in cache, return it to client
  205. if ![$self is-consistent $cl $type $pageid] {
  206. # Page expired and is being refetched, waiting...
  207. if ![$self refetch-pending $cl $type $pageid] {
  208. $self refetch $cl $type $pageid
  209. }
  210. return
  211. }
  212. set server [lindex [split $pageid :] 0]
  213. $self evTrace E HIT p $pageid c [$cl id] s [$server id]
  214. # XXX don't send any response here. Classify responses according
  215. # to request type.
  216. eval $self answer-request-$type $cl $pageid [$self get-page $pageid]
  217. }
  218. # A response may come from: 
  219. # (1) a missed client request,
  220. Http/Cache instproc get-response-GET { server pageid args } {
  221. array set data $args
  222. if ![info exists data(noc)] {
  223. # Cacheable page, continue...
  224. if ![$self exist-page $pageid] {
  225. # Cache the page if it's not in the pool
  226. eval $self enter-page $pageid $args
  227. $self evTrace E ENT p $pageid m $data(modtime) 
  228. z $data(size) s [$server id]
  229. } else {
  230. $self instvar id_ ns_
  231. # A pushed page may come before a response!
  232. puts stderr "At [$ns_ now], cache $id_ has requested a page which it already has."
  233. }
  234. }
  235. # If non-cacheable page, don't cache the page. However, still need to
  236. # answer all pending requests
  237. eval $self answer-pending-requests $pageid $args
  238. $self instvar stat_
  239. incr stat_(barrival) $data(size)
  240. $self instvar node_
  241. $node_ color "blue" ;# valid page
  242. }
  243. Http/Cache instproc answer-pending-requests { pageid args } {
  244. $self instvar creq_ pending_
  245. array set data $args
  246. if [info exists creq_($pageid)] {
  247. # Forward the new page to every client that has requested it
  248. foreach clt $creq_($pageid) {
  249. set tmp [split $clt /]
  250. set cl [lindex $tmp 0]
  251. set type [lindex $tmp 1]
  252. eval $self answer-request-$type $cl $pageid $args
  253. }
  254. unset creq_($pageid)
  255. unset pending_($pageid)
  256. } else {
  257. unset pending_($pageid)
  258. }
  259. }
  260. Http/Cache instproc answer-request-GET { cl pageid args } {
  261. # In response to a GET, we should always return
  262. # our copy of the page.
  263. array set data $args
  264. $self send $cl $data(size) 
  265. "$cl get-response-GET $self $pageid $args"
  266. $self evTrace E SND c [$cl id] p $pageid z $data(size)
  267. }
  268. #----------------------------------------------------------------------
  269. # Cache with consistency protocol based on TTL
  270. #----------------------------------------------------------------------
  271. Class Http/Cache/TTL -superclass Http/Cache
  272. Http/Cache/TTL set updateThreshold_ 0.1
  273. Http/Cache/TTL instproc init args {
  274. eval $self next $args
  275. # Default value
  276. $self instvar thresh_
  277. set thresh_ [Http/Cache/TTL set updateThreshold_]
  278. }
  279. Http/Cache/TTL instproc set-thresh { th } {
  280. $self instvar thresh_
  281. set thresh_ $th
  282. }
  283. # XXX we should store modtime of IMS requests somewhere. Then we can check 
  284. # if that modtime matches this cache's newest modtime when it gets an IMS
  285. # response back from the server
  286. Http/Cache/TTL instproc answer-request-IMS { client pageid args } {
  287. if ![$self exist-page $pageid] {
  288. error "At [$ns_ now], cache [$self id] gets an IMS of a non-cacheable page."
  289. }
  290. set mt [$self get-modtime $pageid]
  291. if ![$client exist-page $pageid] {
  292. error "client [$client id] IMS a page which it doesn't have"
  293. }
  294. if {$mt < [$client get-modtime $pageid]} {
  295. error "client [$client id] IMS a newer page"
  296. }
  297. if {$mt > [$client get-modtime $pageid]} {
  298. # We should send back the new page, even if we got a 
  299. # "not-modified-since"
  300. set pginfo [$self get-page $pageid]
  301. set size [$self get-size $pageid]
  302. } else {
  303. set size [$self get-invsize]
  304. set pginfo "size $size modtime $mt time [$self get-cachetime $pageid]"
  305. }
  306. $self evTrace E SND c [$client id] t IMS z $size
  307. $self send $client $size 
  308. "$client get-response-IMS $self $pageid $pginfo"
  309. }
  310. Http/Cache/TTL instproc get-response-IMS { server pageid args } {
  311. $self instvar ns_
  312. # Alex cache
  313. # Invalidate when:(CurTime-LastCheckTime) > Thresh*(CurTime-CreateTime)
  314. array set data $args
  315. if {$data(modtime) > [$self get-modtime $pageid]} {
  316. # Newer page, cache it
  317. eval $self enter-page $pageid $args
  318. $self evTrace E ENT p $pageid m [$self get-modtime $pageid] 
  319.     z [$self get-size $pageid] s [$server id]
  320. # XXX Set cache entry time to server's entry time so that
  321. # we would have the same expiration time
  322. $self set-cachetime $pageid $data(time)
  323. } else {
  324. # Update entry last validation time
  325. $self set-cachetime $pageid [$ns_ now]
  326. }
  327. eval $self answer-pending-requests $pageid [$self get-page $pageid]
  328. # Compute total bytes arrived
  329. $self instvar stat_
  330. incr stat_(barrival) $data(size)
  331. }
  332. Http/Cache/TTL instproc is-expired { pageid } {
  333. $self instvar thresh_ ns_
  334. set cktime [expr [$ns_ now] - [$self get-cachetime $pageid]]
  335. set age [expr ([$ns_ now] - [$self get-modtime $pageid]) * $thresh_]
  336. if {$cktime <= $age} {
  337. # Not expired
  338. return 0
  339. }
  340. return 1
  341. }
  342. Http/Cache/TTL instproc is-consistent { cl type pageid } { 
  343. return ![$self is-expired $pageid]
  344. }
  345. Http/Cache/TTL instproc refetch-pending { cl type pageid } {
  346. # Page expired, validate it
  347. $self instvar creq_ 
  348. if [info exists creq_($pageid)] {
  349. if [regexp $cl:* $creq_($pageid)] {
  350. # This page already requestsed by this client
  351. return 1
  352. }
  353. # This page is already requested by other clients. Add 
  354. # the new client to the requester list, do not request it again
  355. lappend creq_($pageid) $cl/$type
  356. return 1
  357. }
  358. # Set up a refetch pending state
  359. lappend creq_($pageid) $cl/$type
  360. return 0
  361. }
  362. Http/Cache/TTL instproc refetch { cl type pageid } {
  363. $self instvar parent_
  364. # Send an If-Modified-Since
  365. set server [lindex [split $pageid :] 0]
  366. set size [$self get-imssize]
  367. if [info exists parent_] {
  368. set server $parent_
  369. }
  370. # Compute how many IMSs have been sent so far
  371. $self instvar stat_
  372. incr stat_(ims-num)
  373. $self evTrace E IMS p $pageid c [$cl id] s [$server id] z $size 
  374. t [$self get-cachetime $pageid] m [$self get-modtime $pageid]
  375. $self send-request $server IMS $pageid $size 
  376. modtime [$self get-modtime $pageid]
  377. return 0
  378. }
  379. # Old style TTL, using a single fixed threshold
  380. Class Http/Cache/TTL/Plain -superclass Http/Cache/TTL
  381. Http/Cache/TTL/Plain set updateThreshold_ 100
  382. Http/Cache/TTL/Plain instproc init { args } {
  383. eval $self next $args
  384. $self instvar thresh_
  385. set thresh_ [[$self info class] set updateThreshold_]
  386. }
  387. Http/Cache/TTL/Plain instproc is-expired { pageid } {
  388. $self instvar ns_ thresh_
  389. set cktime [expr [$ns_ now] - [$self get-cachetime $pageid]]
  390. if {$cktime < $thresh_} {
  391. return 0
  392. }
  393. return 1
  394. }
  395. Class Http/Cache/TTL/Omniscient -superclass Http/Cache/TTL
  396. # Assume every cache has exact knowledge of when a page will change
  397. Http/Cache/TTL/Omniscient instproc is-expired { pageid } {
  398. $self instvar ns_ 
  399. set nmt [expr [$self get-modtime $pageid] + [$self get-age $pageid]]
  400. if {[$ns_ now] >= $nmt} {
  401. return 1
  402. return 0
  403. }
  404. #----------------------------------------------------------------------
  405. # Http cache with invalidation -- Base Class
  406. #----------------------------------------------------------------------
  407. Http/Cache/Inval instproc mark-invalid {} {
  408. $self instvar node_
  409. $node_ color "red"
  410. }
  411. Http/Cache/Inval instproc mark-valid {} {
  412. $self instvar node_ 
  413. $node_ color "blue"
  414. }
  415. Http/Cache/Inval instproc mark-leave {} {
  416. $self instvar node_ 
  417. $node_ add-mark down "cyan"
  418. }
  419. Http/Cache/Inval instproc mark-rejoin {} {
  420. $self instvar node_ 
  421. $node_ delete-mark down
  422. }
  423. Http/Cache/Inval instproc answer-request-REF { cl pageid args } {
  424. if ![$self exist-page $pageid] {
  425. error "At [$ns_ now], cache [$self id] gets a REF of a non-cacheable page."
  426. }
  427. # Send my new page back
  428. set pginfo [$self get-page $pageid]
  429. set size [$self get-size $pageid]
  430. $self evTrace E SND c [$cl id] t REF p $pageid z $size
  431. $self send $cl $size 
  432. "$cl get-response-REF $self $pageid $pginfo"
  433. }
  434. Http/Cache/Inval instproc get-response-GET { server pageid args } {
  435. # Check sstate
  436. set sid [[lindex [split $pageid :] 0] id]
  437. set cid [$server id]
  438. $self check-sstate $sid $cid
  439. eval $self next $server $pageid $args
  440. }
  441. # Only get the new page cached, do nothing else
  442. Http/Cache/Inval instproc get-response-REF { server pageid args } {
  443. $self instvar creq_ id_ 
  444. # Check sstate
  445. set sid [[lindex [split $pageid :] 0] id]
  446. set cid [$server id]
  447. $self check-sstate $sid $cid
  448. array set data $args
  449. if {[$self get-modtime $pageid] > $data(modtime)} {
  450. # XXX We may get an old page because we are doing full TCP
  451. # and an update is sent *during* a regular refetch, which is 
  452. # sent through several smaller packets. 
  453. #$self instvar ns_
  454. #error "At [$ns_ now], cache $self ($id_) refetched an old page
  455. #$pageid ($data(modtime), new time [$self get-modtime $pageid])
  456. #from [$server id]"
  457. puts stderr "At [$ns_ now], cache $self ($id_) refetched an old page
  458. $pageid ($data(modtime), new time [$self get-modtime $pageid])
  459. from [$server id]"
  460. # Do nothing; send back the newer page
  461. } else {
  462. # The page is re-validated by replacing the old entry
  463. eval $self enter-page $pageid $args
  464. $self evTrace E UPD p $pageid m [$self get-modtime $pageid] 
  465. z [$self get-size $pageid] s [$server id]
  466. }
  467. eval $self answer-pending-requests $pageid [$self get-page $pageid]
  468. $self instvar node_ marks_ ns_
  469. set mk [lindex $marks_($pageid) 0]
  470. $node_ delete-mark $mk
  471. set marks_($pageid) [lreplace $marks_($pageid) 0 0]
  472. $node_ color "blue"
  473. }
  474. # Always consistent?
  475. Http/Cache/Inval instproc is-consistent { cl type pageid } {
  476. return [$self is-valid $pageid]
  477. }
  478. Http/Cache/Inval instproc refetch-pending { cl type pageid } {
  479. # Invalid page, prepare a refetch. 
  480. $self instvar creq_ 
  481. if [info exists creq_($pageid)] {
  482. if [regexp $cl:* $creq_($pageid)] {
  483. # This page already requestsed by this client
  484. return 1
  485. }
  486. # This page already requested by other clients, add ourselves
  487. # to the returning list and return
  488. lappend creq_($pageid) $cl/$type
  489. return 1
  490. }
  491. # Setup a refetch pending state
  492. lappend creq_($pageid) $cl/$type
  493. return 0
  494. }
  495. # Send a refetch. Forward the request to our parent
  496. Http/Cache/Inval instproc refetch { cl type pageid } {
  497. $self instvar parent_
  498. set size [$self get-refsize]
  499. set server [lindex [split $pageid :] 0]
  500. if [info exists parent_] {
  501. set par $parent_
  502. } else {
  503. # We are the root cache (TLC), directly contact the 
  504. # web server
  505. set par $server
  506. }
  507. $self evTrace E REF p $pageid s [$server id] z $size
  508. $self send-request $par REF $pageid $size
  509. $self instvar node_ marks_ ns_
  510. lappend marks_($pageid) $pageid:[$ns_ now]
  511. $node_ add-mark $pageid:[$ns_ now] "brown"
  512. }
  513. #----------------------------------------------------------------------
  514. # Invalidation cache with multicast heartbeat invalidation
  515. #----------------------------------------------------------------------
  516. Http/Cache/Inval/Mcast instproc init args {
  517. eval $self next $args
  518. $self add-to-map
  519. }
  520. # When we enter a new page into cache, we'll have to register the server
  521. # in case we haven't know anything about it. The right place to do it 
  522. # is in get-response-GET, because a cache will only enter a new page 
  523. # after a cache miss, where it issues a GET.
  524. Http/Cache/Inval/Mcast instproc get-response-GET { server pageid args } {
  525. eval $self next $server $pageid $args
  526. # XXX Assume once server-neighbor cache relationship is fixed, they
  527. # never change.
  528. #  debug 1
  529. set sid [[lindex [split $pageid :] 0] id]
  530. set cid [$server id]
  531. $self register-server $cid $sid
  532. }
  533. Http/Cache/Inval/Mcast instproc set-parent { parent } {
  534. $self next $parent
  535. # Establish a cache entry in state table
  536. $self cmd set-parent $parent
  537. }
  538. # I'm a listener (child)
  539. Http/Cache/Inval/Mcast instproc join-inval-group { group } {
  540. $self instvar invalListener_ invListenGroup_ ns_ node_
  541. if [info exists invalListener_] {
  542. return
  543. }
  544. set invalListener_ [new Agent/HttpInval]
  545. set invListenGroup_ $group
  546. $invalListener_ set dst_addr_ $group
  547. $invalListener_ set dst_port_ 0
  548. $self add-inval-listener $invalListener_
  549. $ns_ attach-agent $node_ $invalListener_
  550. # XXX assuming simulator already started
  551. $node_ join-group $invalListener_ $group
  552. }
  553. # I'm a sender (parent)
  554. Http/Cache/Inval/Mcast instproc init-inval-group { group } {
  555. $self instvar invalSender_ invSndGroup_ ns_ node_
  556. if [info exists invalSender_] {
  557. return
  558. }
  559. set invalSender_ [new Agent/HttpInval]
  560. set invSndGroup_ $group
  561. $invalSender_ set dst_addr_ $group
  562. $invalSender_ set dst_port_ 0
  563. $self add-inval-sender $invalSender_
  564. $ns_ attach-agent $node_ $invalSender_
  565. $node_ join-group $invalSender_ $group
  566. # XXX We should put this somewhere else... But where???
  567. $self start-hbtimer
  568. }
  569. # Another "breakdown" version of parent-cache() is in cache-miss()
  570. Http/Cache/Inval/Mcast instproc parent-cache { server } {
  571. $self instvar parent_
  572. set par [$self cmd parent-cache [$server id]]
  573. if {$par == ""} {
  574. # (par == "") means parent cache in the virtual distribution
  575. # tree is the default, which is parent_
  576. if [info exists parent_] {
  577. set par $parent_
  578. } else {
  579. # We are the root cache (TLC), directly contact the 
  580. # web server
  581. set par $server
  582. }
  583. }
  584. return $par
  585. }
  586. # Send a refetch.
  587. # We should ask our parent in the virtual distribution tree 
  588. # of the corresponding web server, instead of our parent in the 
  589. # cache hierarchy.
  590. Http/Cache/Inval/Mcast instproc refetch { cl type pageid } {
  591. set size [$self get-refsize]
  592. set server [lindex [split $pageid :] 0]
  593. set par [$self parent-cache $server]
  594. $self evTrace E REF p $pageid s [$server id] z $size
  595. $self send-request $par REF $pageid $size
  596. $self instvar node_ marks_ ns_
  597. lappend marks_($pageid) $pageid:[$ns_ now]
  598. $node_ add-mark $pageid:[$ns_ now] "brown"
  599. }
  600. # Cache miss, get it from our parent cache in the virtual distribution
  601. # tree of the web server
  602. Http/Cache/Inval/Mcast instproc cache-miss { cl type pageid } {
  603. $self instvar parent_ pending_ creq_ ;# pending client requests
  604. lappend creq_($pageid) $cl/$type
  605. # XXX If there's a previous requests going on we won't send another
  606. # request for the same page.
  607. if [info exists pending_($pageid)] {
  608. return
  609. }
  610. # Page not found, contact parent and get the page.
  611. set size [$self get-reqsize]
  612. set server [lindex [split $pageid :] 0]
  613. $self evTrace E MISS p $pageid c [$cl id] s [$server id] z $size
  614. # We directly query the server map without using TCL's version
  615. # of parent-cache() to mask details...
  616. set par [$self cmd parent-cache [$server id]]
  617. if {$par == ""} {
  618. if [info exists parent_] {
  619. # Use default server map, i.e., parent cache
  620. set par $parent_
  621. } else {
  622. # This is a TLC, and the request is for another server
  623. # in another hierarchy (because we don't have it in our
  624. # server map, nor do we have a parent cache). Now we 
  625. # need to find out what's the corresponding TLC of 
  626. # the web server so as to setup invalidation path.
  627. #
  628. # Send a direct request to server to ask about TLC
  629. $self instvar ns_ id_
  630. #puts "[$ns_ now]: $id_ send TLC"
  631. $self send-request $server TLC $pageid $size
  632. # We'll send another request to the TLC after we get 
  633. # its addr
  634. return
  635. }
  636. }
  637. $self send-request $par $type $pageid $size
  638. }
  639. # This allows a server passes invalidation to any cache via unicast
  640. # XXX Whenever a node only wants to do an invalidation, call "cmd recv-inv"
  641. Http/Cache/Inval/Mcast instproc invalidate { pageid modtime } {
  642. if [$self recv-inv $pageid $modtime] {
  643. # Unicast invalidation to parent.
  644. $self instvar parent_ 
  645. if ![info exists parent_] {
  646. # This must be a root cache, should we do anything? 
  647. return
  648. }
  649. set size [$self get-invsize]
  650. $self evTrace E SND t INV c [$parent_ id] p $pageid z $size
  651. # Mark invalidation packet as another fid
  652. set agent [[$self get-cnc $parent_] agent]
  653. set fid [$agent set fid_]
  654. $agent set fid_ [Http set PINV_FID_]
  655. $self send $parent_ $size 
  656. "$parent_ invalidate $pageid $modtime"
  657. $agent set fid_ $fid
  658. }
  659. }
  660. Http/Cache/Inval/Mcast instproc get-request { cl type pageid args } {
  661. eval $self next $cl $type $pageid $args
  662. if {(($type == "GET") || ($type == "REF")) && 
  663. [$self exist-page $pageid]} {
  664. $self count-request $pageid
  665. if [$self is-unread $pageid] {
  666. $self send-req-notify $pageid
  667. $self set-read $pageid
  668. }
  669. }
  670. }
  671. # Do the same thing as if getting a request
  672. Http/Cache/Inval/Mcast instproc get-req-notify { pageid } {
  673. $self count-request $pageid
  674. if [$self is-unread $pageid] {
  675. # Continue to forward the request only if our page is 
  676. # also unread
  677. $self set-read $pageid
  678. $self send-req-notify $pageid
  679. }
  680. }
  681. # Request notification goes along a single path in the virtual distribution
  682. # tree towards the web server. It's not multicast to anybody else
  683. Http/Cache/Inval/Mcast instproc send-req-notify { pageid } {
  684. set server [lindex [split $pageid :] 0]
  685. set par [$self parent-cache $server]
  686. $self send $par [$self get-ntfsize] "$par get-req-notify $pageid"
  687. }
  688. # (1) setup an invalidation record is set to invalidate my children; 
  689. # (2) Unicast the new page to my parent;
  690. # (3) Update my own page records
  691. # (4) Setting up a repair group to send out the new page (once and for all)
  692. Http/Cache/Inval/Mcast instproc push-update { pageid args } {
  693. # Update page, possibly push the page to children
  694. if [eval $self recv-push $pageid $args] {
  695. # XXX We should probably check if we have pending request for 
  696. # this page. If so, we should use this pushed page to answer 
  697. # those pending requests, and then mark this page as read.
  698. # unicast push to parent
  699. $self instvar parent_ 
  700. if [info exists parent_] {
  701. # If we are root, don't forward the data packet to
  702. # anybody. Otherwise unicast the new page to my parent
  703. set pginfo [$self get-page $pageid]
  704. set size [$self get-size $pageid]
  705. $self evTrace E UPD c [$parent_ id] p $pageid z $size
  706. $self send $parent_ $size 
  707. "$parent_ push-update $pageid $pginfo"
  708. }
  709. $self push-children $pageid
  710. }
  711. }
  712. Http/Cache/Inval/Mcast instproc init-update-group { group } {
  713. $self instvar ns_ node_ updSender_ updSendGroup_
  714. # Allow a cache to have multiple update groups. 
  715. set snd [new Agent/HttpInval]
  716. $snd set dst_addr_ $group
  717. $snd set dst_port_ 0
  718. $self add-upd-sender $snd
  719. $ns_ attach-agent $node_ $snd
  720. $node_ join-group $snd $group
  721. }
  722. Http/Cache/Inval/Mcast instproc join-update-group { group }  {
  723. $self instvar updListener_ updListenGroup_ ns_ node_
  724. set updListenGroup_ $group
  725. # One cache can only receive from one update group at a time
  726. if ![info exists updListener_] {
  727. set updListener_ [new Agent/HttpInval]
  728. $self add-upd-listener $updListener_
  729. $updListener_ set dst_addr_ $updListenGroup_
  730. $updListener_ set dst_port_ 0
  731. $ns_ attach-agent $node_ $updListener_
  732. }
  733. $node_ join-group $updListener_ $updListenGroup_
  734. # $node_ add-mark "Updating" "Orange"
  735. }
  736. Http/Cache/Inval/Mcast instproc leave-update-group {} {
  737. $self instvar updListener_ updListenGroup_ ns_ node_
  738. if ![info exists updListener_] {
  739. return
  740. }
  741. $node_ leave-group $updListener_ $updListenGroup_
  742. $node_ delete-mark "Updating"
  743. }
  744. # Set up a unicast heartbeat connection
  745. Http/Cache/Inval/Mcast instproc setup-unicast-hb {} {
  746. Http instvar TRANSPORT_
  747. $self instvar node_ ns_
  748. set snk [new Agent/TCP/$TRANSPORT_]
  749. $snk set fid_ [Http set HB_FID_]
  750. $ns_ attach-agent $node_ $snk
  751. $snk listen
  752. set wrapper [new Application/TcpApp/HttpInval $snk]
  753. $wrapper set-app $self
  754. return $wrapper
  755. }
  756. # Establish state for server. Propagate until Top-Level Cache is reached
  757. # Set up heartbeat connection along the way
  758. Http/Cache/Inval/Mcast instproc server-join { server cache } {
  759. $self cmd join [$server id] $cache
  760. #puts "Server [$server id] joins cache [$self id]"
  761. $self instvar parent_
  762. if ![info exists parent_] {
  763. return
  764. }
  765. $self send $parent_ [$self get-joinsize] 
  766. "$parent_ server-join $server $self"
  767. # Establishing a tcp connection. 
  768. Http instvar TRANSPORT_
  769. $self instvar ns_ node_
  770. set tcp [new Agent/TCP/$TRANSPORT_]
  771. $tcp set fid_ [Http set HB_FID_]
  772. $ns_ attach-agent $node_ $tcp
  773. set dst [$parent_ setup-unicast-hb]
  774. set snk [$dst agent]
  775. $ns_ connect $tcp $snk
  776. #$tcp set dst_ [$snk set addr_] 
  777. $tcp set window_ 100
  778. set wrapper [new Application/TcpApp/HttpInval $tcp]
  779. $wrapper connect $dst
  780. $wrapper set-app $self
  781. $self set-pinv-agent $wrapper
  782. # If we haven't started it yet, start it.
  783. $self start-hbtimer
  784. }
  785. Http/Cache/Inval/Mcast instproc request-mpush { page } {
  786. $self instvar mpush_refresh_ ns_ hb_interval_
  787. if [info exists mpush_refresh_($page)] {
  788. # The page is already set as mandatory push, ignore it
  789. return
  790. }
  791. $self set-mandatory-push $page
  792. set server [lindex [split $page :] 0]
  793. set cache [$self parent-cache $server]
  794. set mpush_refresh_($page) [$ns_ at [expr [$ns_ now] + $hb_interval_] 
  795. "$self send-refresh-mpush $cache $page"]
  796. # Forward the push request towards the web server
  797. $self send $cache [$self get-mpusize] "$cache request-mpush $page"
  798. }
  799. Http/Cache/Inval/Mcast instproc refresh-mpush { page } {
  800. $self cmd set-mandatory-push $page
  801. }
  802. Http/Cache/Inval/Mcast instproc send-refresh-mpush { cache page } {
  803. $self instvar mpush_refresh_ ns_ hb_interval_
  804. $self send $cache [$self get-mpusize] "$cache refresh-mpush $page"
  805. set mpush_refresh_($page) [$ns_ at [expr [$ns_ now] + $hb_interval_] 
  806. "$self send-refresh-mpush $cache $page"]
  807. }
  808. # XXX This is used when a mpush is timed out, where we don't need to 
  809. # send explicit teardown, etc. 
  810. Http/Cache/Inval/Mcast instproc cancel-mpush-refresh { page } {
  811. $self instvar mpush_refresh_ ns_ 
  812. if [info exists mpush_refresh_($page)] {
  813. $ns_ cancel $mpush_refresh_($page)
  814. #puts "[$ns_ now]: Cache [$self id] stops mpush"
  815. } else {
  816. error "Cache [$self id]: No mpush to stop!"
  817. }
  818. }
  819. Http/Cache/Inval/Mcast instproc stop-mpush { page } {
  820. # Cancel refresh messages
  821. $self cancel-mpush-refresh $page
  822. # Clear page push status
  823. $self cmd stop-mpush $page
  824. # Send explicit message to stop mpush
  825. set server [lindex [split $page :] 0]
  826. set cache [$self parent-cache $server]
  827. $self send $cache [$self get-mpusize] "$cache stop-mpush $page"
  828. }
  829. #
  830. # Support for multiple hierarchies
  831. #
  832. # Top-Level Caches (TLCs) need to exchange invalidations with each other,
  833. # so they are both sender and receiver in this multicast group. 
  834. Http/Cache/Inval/Mcast instproc join-tlc-group { group } {
  835. $self instvar tlcAgent_ tlcGroup_ ns_ node_
  836. if [info exists tlcAgent_] {
  837. return 
  838. }
  839. set tlcAgent_ [new Agent/HttpInval]
  840. set tlcGroup_ $group
  841. $tlcAgent_ set dst_addr_ $group
  842. $tlcAgent_ set dst_port_ 0
  843. $self add-inval-sender $tlcAgent_
  844. $self add-inval-listener $tlcAgent_
  845. $ns_ attach-agent $node_ $tlcAgent_
  846. $node_ join-group $tlcAgent_ $group
  847. }
  848. Http/Cache/Inval/Mcast instproc get-response-TLC { server pageid tlc } {
  849. # Continue query...
  850. #  debug 1
  851. $self register-server [$tlc id] [$server id]
  852. $self instvar ns_ id_
  853. # puts "[$ns_ now]: Cache $id_ knows server [$server id] -> tlc [$tlc id]"
  854. $self send-request $tlc GET $pageid [$self get-reqsize]
  855. }
  856. #----------------------------------------------------------------------
  857. # Http/Cache/Inval/Mcast/Perc
  858. # Multicast invalidation + two way liveness message + invalidation 
  859. # filtering. Must be used with Http/Server/Inval/Ucast/Perc
  860. # Requires C++ support. This is why we have this long name. :( 
  861. #
  862. # Procedures: 
  863. # - Server's new page: the server injects it into the cache hierarchy by
  864. #   sending it to its parent cache, which in turn forwards it up the tree.
  865. # - Every cache keeps a cost for each cached page. 
  866. #----------------------------------------------------------------------
  867. # XXX Do not check-sstate{} when getting a response. Because we are doing 
  868. # direct request, those responses will always come from the server
  869. Http/Cache/Inval/Mcast/Perc instproc check-sstate {sid cid} {
  870. $self instvar direct_request_
  871. if !$direct_request_ {
  872. # If not using direct request, check sstate
  873. $self cmd check-sstate $sid $cid
  874. }
  875. }
  876. # Because we are doing direct request, we'll get a lot of responses 
  877. # directly from the server, and we'll have cid == sid. We don't want to
  878. # register this into our server map, because the server map is used 
  879. # for forwarding pro formas. Therefore, we wrap up register-server to
  880. # direct requests to all our *UNKNOWN* servers to our parent. 
  881. #
  882. # Note this won't disrupt server entries via JOIN, because they are 
  883. # established before any request is sent.
  884. Http/Cache/Inval/Mcast/Perc instproc register-server {cid sid} {
  885. $self instvar parent_ direct_request_
  886. #  debug 1
  887. if {$direct_request_ && [info exists parent_]} {
  888. $self cmd register-server [$parent_ id] $sid
  889. }
  890. # Allows direct request
  891. Http/Cache/Inval/Mcast/Perc instproc cache-miss { cl type pageid } {
  892. $self instvar direct_request_
  893. if !$direct_request_ {
  894. # If not use direct request, fall back to previous method
  895. $self next $cl $type $pageid
  896. return
  897. }
  898. # If use direct request, send a request to the web server to ask 
  899. # for the page, and then send a pro forma when get the request 
  900. $self instvar parent_ pending_ creq_ ;# pending client requests
  901. $self instvar dreq_ ;# pending direct requests
  902. lappend creq_($pageid) $cl/$type
  903. # XXX If there's a previous requests going on we won't send another
  904. # request for the same page.
  905. if [info exists pending_($pageid)] {
  906. return
  907. }
  908. $self instvar dreq_
  909. set dreq_($pageid) 1
  910. # Page not found, directly contact the server and get the page. 
  911. set server [lindex [split $pageid :] 0]
  912. set size [$self get-reqsize]
  913. $self evTrace E MISS p $pageid c [$cl id] s [$server id] z $size
  914. $self send-request $server $type $pageid $size
  915. }
  916. # Allows direct request
  917. Http/Cache/Inval/Mcast/Perc instproc refetch { cl type pageid } {
  918. $self instvar direct_request_
  919. if !$direct_request_ {
  920. $self next $cl $type $pageid
  921. return
  922. }
  923. $self instvar dreq_
  924. set dreq_($pageid) 1
  925. set size [$self get-refsize]
  926. set server [lindex [split $pageid :] 0]
  927. $self evTrace E REF p $pageid s [$server id] z $size 
  928. $self send-request $server REF $pageid $size
  929. $self instvar node_ marks_ ns_
  930. lappend marks_($pageid) $pageid:[$ns_ now]
  931. $node_ add-mark $pageid:[$ns_ now] "brown"
  932. }
  933. # Whenever get a request, send a pro forma up
  934. Http/Cache/Inval/Mcast/Perc instproc get-response-GET { server pageid args } {
  935. # First, answer children's requests, etc.
  936. eval $self next $server $pageid $args
  937. # Then send a pro forma if it's a direct request
  938. $self instvar dreq_ 
  939. if [info exists dreq_($pageid)] {
  940. # If this page is result of a direct request, send a pro forma
  941. eval $self send-proforma $pageid $args
  942. unset dreq_($pageid)
  943. }
  944. }
  945. # Same treatment as get-response-GET
  946. Http/Cache/Inval/Mcast/Perc instproc get-response-REF { server pageid args } {
  947. eval $self next $server $pageid $args
  948. $self instvar dreq_
  949. if [info exists dreq_($pageid)] {
  950. eval $self send-proforma $pageid $args
  951. unset dreq_($pageid)
  952. }
  953. }
  954. # XXX We need special handling for multiple hierarchies. If we cannot find 
  955. # the server in our server map, we directly call the server's routine to 
  956. # find out its TLC. This doesn't make the simulation artificial, though, 
  957. # because in our previous direct response from the server, we could have
  958. # easily gotten its TLC. 
  959. Http/Cache/Inval/Mcast/Perc instproc send-proforma { pageid args } {
  960. set server [lindex [split $pageid :] 0]
  961. set par [$self parent-cache $server]
  962. if {$par == $server} {
  963. # If we are the primary cache, don't send anything
  964. return
  965. } elseif {$par == ""} {
  966. # XXX 
  967. # We are the TLC, and we don't have a server entry. This 
  968. # means that the server resides in another hierarchy. 
  969. # Query the global server-to-TLC map to unicast this 
  970. # pro forma to that TLC...
  971. set par [$server get-tlc]
  972. #puts "TLC [$self id] learned about server [$server id] by pro forma"
  973. }
  974. $self send $par [$self get-pfsize] 
  975. "$par recv-proforma $self $pageid [join $args]"
  976. $self evTrace E SPF p $pageid c [$par id]
  977. }
  978. Http/Cache/Inval/Mcast/Perc instproc get-response-IMS { server pageid args } {
  979. $self instvar ns_ 
  980. array set data $args
  981. if {$data(modtime) <= [$self get-modtime $pageid]} {
  982. # The page we got from the pro forma is indeed most up-to-date
  983. return
  984. }
  985. # The server has changed the page since the pro forma is sent
  986. # We need to send invalidations to invalidate the page
  987. $self invalidate $pageid 
  988. eval $self enter-page $pageid $args
  989. $self mark-valid
  990. }
  991. Http/Cache/Inval/Mcast/Perc instproc mark-valid-hdr {} {
  992. $self instvar node_
  993. $node_ color "orange"
  994. }
  995. Http/Cache/Inval/Mcast/Perc instproc recv-proforma { cache pageid args } {
  996. $self instvar stat_
  997. # count pro forma as one TLC hit
  998. incr stat_(hit-num)
  999. $self evTrace E RPF p $pageid c [$cache id]
  1000. array set data $args
  1001. if ![$self exist-page $pageid] {
  1002. # Page doesn't exists. Create an entry for page header, and
  1003. # forward it towards the web server
  1004. eval $self enter-metadata $pageid $args
  1005. $self mark-valid-hdr
  1006. set server [lindex [split $pageid :] 0]
  1007. set par [$self parent-cache $server]
  1008. if {$par == $server} {
  1009. # If we are the primary cache, validate this 
  1010. # pro forma by sending an IMS
  1011. $self send-request $par IMS $pageid 
  1012. [$self get-imssize] modtime $data(modtime)
  1013. } else {
  1014. eval $self send-proforma $pageid $args
  1015. }
  1016. } elseif [$self is-valid $pageid] {
  1017. # Valid page, check if this is a newer one
  1018. set mt [$self get-modtime $pageid]
  1019. if {$data(modtime) < $mt} {
  1020. # If the pro forma is older, should invalidate our
  1021. # children so that they'll invalidate their stuff
  1022. $self recv-inv $pageid $data(modtime)
  1023. return
  1024. } elseif {$data(modtime) > $mt} {
  1025. # If the pro forma is about a newer page, 
  1026. # first invalidate our page, so that we have an 
  1027. # invalidation record to let our children know the 
  1028. # page is invalid. Then enter the page metadata.
  1029. # XXX Should check for existence of page content
  1030. $self recv-inv $pageid $data(modtime)
  1031. eval $self enter-metadata $pageid $args
  1032. $self mark-valid-hdr
  1033. eval $self send-proforma $pageid $args
  1034. }
  1035. # Drop the pro forma if it's the same as our page.
  1036. # XXX count the pro forma as a request to this page, and
  1037. # send a request notification towards the web server. 
  1038. # Mark the page as read if it's originally unread.
  1039. $self count-request $pageid
  1040. if [$self is-unread $pageid] {
  1041. $self set-read $pageid
  1042. }
  1043. } else {
  1044. # Invalid page, check if we should set a valid page header
  1045. # so that invalidations will be forwarded.
  1046. array set data $args
  1047. set mt [$self get-modtime $pageid]
  1048. if {$data(modtime) < $mt} {
  1049. # We already have the most up-to-date page, so are
  1050. # our parents. Do nothing
  1051. return
  1052. # The pro forma is newer, put in the new meta-data and 
  1053. # set the page as valid_header but not valid_page
  1054. # Note if a page is invalid, its modtime is that of the 
  1055. # newest page.
  1056. # XXX Should test for the existence of page content by 
  1057. # looking at the size of the pro forma.
  1058. eval $self enter-metadata $pageid $args
  1059. $self mark-valid-hdr
  1060. eval $self send-proforma $pageid $args
  1061. }
  1062. }