s3-prefork: Improve heuristics
[idra/samba.git] / source3 / lib / server_prefork_util.c
1 /*
2    Unix SMB/Netbios implementation.
3    Prefork Helpers
4    Copyright (C) Simo Sorce <idra@samba.org> 2011
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "lib/server_prefork.h"
22 #include "lib/server_prefork_util.h"
23
24 void pfh_daemon_config(const char *daemon_name,
25                         struct pf_daemon_config *cfg,
26                         struct pf_daemon_config *default_cfg)
27 {
28         int min, max, rate, allow, life;
29
30         min = lp_parm_int(GLOBAL_SECTION_SNUM,
31                                 daemon_name,
32                                 "prefork_min_children",
33                                 default_cfg->min_children);
34         max = lp_parm_int(GLOBAL_SECTION_SNUM,
35                                 daemon_name,
36                                 "prefork_max_children",
37                                 default_cfg->max_children);
38         rate = lp_parm_int(GLOBAL_SECTION_SNUM,
39                                 daemon_name,
40                                 "prefork_spawn_rate",
41                                 default_cfg->spawn_rate);
42         allow = lp_parm_int(GLOBAL_SECTION_SNUM,
43                                 daemon_name,
44                                 "prefork_max_allowed_clients",
45                                 default_cfg->max_allowed_clients);
46         life = lp_parm_int(GLOBAL_SECTION_SNUM,
47                                 daemon_name,
48                                 "prefork_child_min_life",
49                                 default_cfg->child_min_life);
50
51         if (max > cfg->max_children && cfg->max_children != 0) {
52                 cfg->prefork_status |= PFH_NEW_MAX;
53         }
54
55         cfg->min_children = min;
56         cfg->max_children = max;
57         cfg->spawn_rate = rate;
58         cfg->max_allowed_clients = allow;
59         cfg->child_min_life = life;
60 }
61
62 void pfh_manage_pool(struct tevent_context *ev_ctx,
63                      struct messaging_context *msg_ctx,
64                      struct pf_daemon_config *cfg,
65                      struct prefork_pool *pool)
66 {
67         time_t now = time(NULL);
68         int total, avail;
69         int ret, n;
70
71         if ((cfg->prefork_status & PFH_NEW_MAX) &&
72             !(cfg->prefork_status & PFH_ENOSPC)) {
73                 ret = prefork_expand_pool(pool, cfg->max_children);
74                 if (ret == ENOSPC) {
75                         cfg->prefork_status |= PFH_ENOSPC;
76                 }
77                 cfg->prefork_status &= ~PFH_NEW_MAX;
78         }
79
80         total = prefork_count_children(pool, NULL);
81         avail = prefork_count_allowed_connections(pool);
82         DEBUG(10, ("(Pre)Stats: children: %d, allowed connections: %d\n",
83                    total, avail));
84
85         if ((total < cfg->max_children) && (avail < cfg->spawn_rate)) {
86                 n = prefork_add_children(ev_ctx, msg_ctx,
87                                          pool, cfg->spawn_rate);
88                 if (n < cfg->spawn_rate) {
89                         DEBUG(10, ("Attempted to add %d children but only "
90                                    "%d were actually added!\n",
91                                    cfg->spawn_rate, n));
92                 }
93         } else if ((avail - cfg->min_children) >= cfg->spawn_rate) {
94                 /* be a little slower in retiring children, to allow for
95                  * double spikes of traffic to be handled more gracefully */
96                 n = (cfg->spawn_rate / 2) + 1;
97                 if (n > cfg->spawn_rate) {
98                         n = cfg->spawn_rate;
99                 }
100                 if ((total - n) < cfg->min_children) {
101                         n = total - cfg->min_children;
102                 }
103                 if (n >= 0) {
104                         prefork_retire_children(pool, n,
105                                                 now - cfg->child_min_life);
106                 }
107         }
108
109         /* total/avail may have just been changed in the above if/else */
110         total = prefork_count_children(pool, NULL);
111         avail = prefork_count_allowed_connections(pool);
112         if ((total == cfg->max_children) && (avail < cfg->spawn_rate)) {
113                 n = avail;
114                 while (avail < cfg->spawn_rate) {
115                         prefork_increase_allowed_clients(pool,
116                                                 cfg->max_allowed_clients);
117                         avail = prefork_count_allowed_connections(pool);
118                         /* if avail didn't change do not loop forever */
119                         if (n == avail) break;
120                         n = avail;
121                 }
122         } else if (avail > total + cfg->spawn_rate) {
123                 n = avail;
124                 while (avail > total + cfg->spawn_rate) {
125                         prefork_decrease_allowed_clients(pool);
126                         avail = prefork_count_allowed_connections(pool);
127                         /* if avail didn't change do not loop forever */
128                         if (n == avail) break;
129                         n = avail;
130                 }
131         }
132
133         DEBUG(10, ("Stats: children: %d, allowed connections: %d\n",
134                   prefork_count_children(pool, NULL),
135                   prefork_count_allowed_connections(pool)));
136 }
137
138 void pfh_client_terminated(struct pf_worker_data *pf)
139 {
140         if (pf->num_clients >= 0) {
141                 pf->num_clients--;
142         } else {
143                 if (pf->status != PF_WORKER_EXITING) {
144                         DEBUG(1, ("Invalid num clients, stopping!\n"));
145                 }
146                 pf->status = PF_WORKER_EXITING;
147                 pf->num_clients = -1;
148         }
149 }
150
151 bool pfh_child_allowed_to_accept(struct pf_worker_data *pf)
152 {
153         if (pf->status == PF_WORKER_EXITING ||
154             pf->status == PF_WORKER_ACCEPTING) {
155                 return false;
156         }
157
158         return (pf->num_clients < pf->allowed_clients);
159 }