ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/ext/map-scheduler.ext
Revision: 1.41
Committed: Fri Aug 24 01:12:11 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
CVS Tags: rel-2_2
Changes since 1.40: +2 -1 lines
Log Message:
keep maps in memory for at least 1.5 ticks.

background: rabies can cause the high watermark for load to be quickly exceeded, putting
the server in emergency swap mode. if we don't keep maps in memory for at least 1.5 ticks
we will then swap out (player-) visible maps, causing "blinking". by keeping them in memory
for at least a tick we avoid this problem entirely as all visible maps are never idle
longer than a tick.

File Contents

# User Rev Content
1 root 1.37 #! perl # mandatory
2 root 1.1
3     # this extension swaps out maps and resets them, in essence managing
4     # the reset/swap policy of the server.
5 root 1.18 # it also contains the map prefetching logic
6 root 1.1
7 root 1.24 #our $DEACTIVATE_TIMEOUT = 20; # number of seconds after which maps get deactivated to save cpu
8 root 1.34 our $SWAP_TIMEOUT = $cf::CFG{swap_timeout} || 300; # number of seconds after which inactive maps get swapped out
9     our $SCHEDULE_INTERVAL = $cf::CFG{schedule_interval} || .8; # time the map scheduler sleeps between runs
10     our $SAVE_TIMEOUT = $cf::CFG{save_timeout} || 30; # save maps every n seconds
11     our $SWAP_LOAD1 = $cf::CFG{swap_load1} || .1; # start aggressively swapping at this load
12     our $SWAP_LOAD2 = $cf::CFG{swap_load2} || .4; # swap as fast as possible at this load
13 root 1.1
14 root 1.17 cf::async_ext {
15 root 1.12 $Coro::current->prio (Coro::PRIO_MIN);
16 root 1.35
17 root 1.14 # load the header of swapped-out maps.
18 root 1.12 # this is not a correctness issue, it simply saves diskspace
19     # because old files will get cleaned up on reset time
20 root 1.35 Coro::Timer::sleep 0.25;
21 root 1.12
22 root 1.36 for my $path (@{ cf::map::tmp_maps or [] }, @{ cf::map::random_maps or [] }) {
23 root 1.38 cf::cede_to_tick;
24 root 1.36 cf::map::find $path;
25 root 1.12 }
26    
27 root 1.35 # now hunt for resettable per-player maps
28     for my $login (@{ cf::player::list_logins or [] }) {
29     for my $path (@{ cf::player::maps $login or [] }) {
30 root 1.38 cf::cede_to_tick;
31 root 1.35
32     $path =~ /^~[^\/]+(\/.*)$/
33     or next; # doh
34    
35     my $base = cf::map::find $1;
36    
37     # skip maps without base maps on the assumption
38     # that those are old, unresettable maps
39     next unless $base;
40    
41     # skip unresettable maps, for speed
42     next if $base->{deny_reset};
43    
44     my $map = cf::map::find $path;
45    
46     if ($map->{deny_reset}) {
47     warn "found noreset map with resettable base map, resetting: $path\n";
48     delete $map->{deny_reset};
49     }
50     }
51     }
52 root 1.12 };
53    
54 root 1.17 our $SCHEDULER = cf::async_ext {
55 root 1.38 my $timer = Coro::Event->timer (after => 1);
56    
57 root 1.1 while () {
58 root 1.38 $timer->interval ($SCHEDULE_INTERVAL);
59     $timer->next unless $cf::LOADAVG > $SWAP_LOAD2;
60 root 1.1
61 root 1.9 # this weird form of iteration over values is used because
62     # the hash changes underneath us frequently, and for
63     # keeps a direct reference to the value without (in 5.8 perls)
64     # keeping a reference, so this is prone to crashes or worse.
65 root 1.8 my @maps = keys %cf::MAP;
66     for (@maps) {
67     my $map = $cf::MAP{$_}
68     or next;
69     $map->valid or next;
70 root 1.9
71 root 1.1 eval {
72     # not yet, because maps might become visible to players nearby
73 root 1.3 # we need to remove the map from %cf::MAP and all tiled map links
74 root 1.1 # if ($last_access + $DEACTIVATE_TIMEOUT <= $cf::RUNTIME) {
75     # $map->deactivate;
76     # delete $map->{active};
77     # }
78     if ($map->should_reset) {
79     $map->reset;
80 root 1.3 } elsif ($map->in_memory == cf::MAP_IN_MEMORY) {
81 root 1.41 my $max_idle = cf::clamp +(cf::lerp $cf::LOADAVG, $SWAP_LOAD1, $SWAP_LOAD2, $SWAP_TIMEOUT, $cf::TICK * 1.5),
82     $cf::TICK * 1.5, $SWAP_TIMEOUT;
83 root 1.34
84 root 1.26 if ($map->last_access + $max_idle <= $cf::RUNTIME && !$map->players) {
85 root 1.25 $map->swap_out;
86 root 1.3 } elsif ($map->{last_save} + $SAVE_TIMEOUT <= $cf::RUNTIME) {
87 root 1.25 $map->save;
88 root 1.39 $map->{last_save} -= rand; # randomise map save times a bit
89 root 1.3 }
90 root 1.1 }
91     };
92     warn $@ if $@;
93 root 1.34 cf::cede_to_tick;
94 root 1.8 };
95 root 1.1 }
96     };
97 root 1.5
98 root 1.1 $SCHEDULER->prio (-2);
99