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

# Content
1 #! perl # mandatory
2
3 # this extension swaps out maps and resets them, in essence managing
4 # the reset/swap policy of the server.
5 # it also contains the map prefetching logic
6
7 #our $DEACTIVATE_TIMEOUT = 20; # number of seconds after which maps get deactivated to save cpu
8 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
14 cf::async_ext {
15 $Coro::current->prio (Coro::PRIO_MIN);
16
17 # load the header of swapped-out maps.
18 # this is not a correctness issue, it simply saves diskspace
19 # because old files will get cleaned up on reset time
20 Coro::Timer::sleep 0.25;
21
22 for my $path (@{ cf::map::tmp_maps or [] }, @{ cf::map::random_maps or [] }) {
23 cf::cede_to_tick;
24 cf::map::find $path;
25 }
26
27 # 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 cf::cede_to_tick;
31
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 };
53
54 our $SCHEDULER = cf::async_ext {
55 my $timer = Coro::Event->timer (after => 1);
56
57 while () {
58 $timer->interval ($SCHEDULE_INTERVAL);
59 $timer->next unless $cf::LOADAVG > $SWAP_LOAD2;
60
61 # 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 my @maps = keys %cf::MAP;
66 for (@maps) {
67 my $map = $cf::MAP{$_}
68 or next;
69 $map->valid or next;
70
71 eval {
72 # not yet, because maps might become visible to players nearby
73 # we need to remove the map from %cf::MAP and all tiled map links
74 # if ($last_access + $DEACTIVATE_TIMEOUT <= $cf::RUNTIME) {
75 # $map->deactivate;
76 # delete $map->{active};
77 # }
78 if ($map->should_reset) {
79 $map->reset;
80 } elsif ($map->in_memory == cf::MAP_IN_MEMORY) {
81 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
84 if ($map->last_access + $max_idle <= $cf::RUNTIME && !$map->players) {
85 $map->swap_out;
86 } elsif ($map->{last_save} + $SAVE_TIMEOUT <= $cf::RUNTIME) {
87 $map->save;
88 $map->{last_save} -= rand; # randomise map save times a bit
89 }
90 }
91 };
92 warn $@ if $@;
93 cf::cede_to_tick;
94 };
95 }
96 };
97
98 $SCHEDULER->prio (-2);
99