Browse Source

Add worker count & load to stats : solve #24 solve #20 solve #19

Yann Weber 4 years ago
parent
commit
b3af92459e
5 changed files with 181 additions and 13 deletions
  1. 3
    0
      include/responder.h
  2. 17
    3
      include/stats.h
  3. 4
    2
      src/ipc.c
  4. 59
    0
      src/responder.c
  5. 98
    8
      src/stats.c

+ 3
- 0
include/responder.h View File

@@ -103,4 +103,7 @@ void pool_sighandler(int signum);
103 103
 /**@brief Handle watchdog alarm signal */
104 104
 void pool_wd_sighandler(int signum);
105 105
 
106
+/**@brief Update SHM data */
107
+void pyfcgi_pool_shm_update(int nworker);
108
+
106 109
 #endif

+ 17
- 3
include/stats.h View File

@@ -31,17 +31,29 @@
31 31
 #define PYFCGI_STATS_SZ (900)
32 32
 
33 33
 typedef struct pyfcgi_stats_s pyfcgi_stats_t;
34
+typedef struct pyfcgi_stats_sample_s pyfcgi_stats_sample_t;
34 35
 typedef struct pyfcgi_stats_shm_s pyfcgi_stats_shm_t;
35 36
 
36 37
 #include "conf.h"
37 38
 #include "logger.h"
38 39
 
40
+/**@brief Stores data for stats on 15min (900s) */
41
+struct pyfcgi_stats_sample_s
42
+{
43
+	/**@brief One sample per seconds */
44
+	int samples[PYFCGI_STATS_SZ];
45
+	/**@brief Current sample */
46
+	int cur;
47
+};
48
+
39 49
 struct pyfcgi_stats_s
40 50
 {
41 51
 	/**@brief Request per seconds on 15 minutes */
42
-	int reqs[PYFCGI_STATS_SZ];
43
-	/**@brief Current request index */
44
-	int cur_req;
52
+	pyfcgi_stats_sample_t reqs;
53
+	/**@brief Worker count */
54
+	pyfcgi_stats_sample_t wcount;
55
+	/**@brief Load */
56
+	pyfcgi_stats_sample_t load;
45 57
 
46 58
 	/**@brief Repeating 1s timer sending SIGALRM */
47 59
 	timer_t timerid;
@@ -111,5 +123,7 @@ int pyfcgi_stats_avg_const(const int[PYFCGI_STATS_SZ], int*, int*, double[4]);
111 123
  * to remove this function... */
112 124
 const char *pyfcgi_stats_buff(const char **, size_t*);
113 125
 
126
+int pyfcgi_stats_get_shm(pyfcgi_stats_shm_t*);
127
+
114 128
 #endif
115 129
 

+ 4
- 2
src/ipc.c View File

@@ -58,7 +58,7 @@ int pyfcgi_IPC_create(pyfcgi_ipc_flag_t flag)
58 58
 	{
59 59
 		flag ^= IPC_SHMST;
60 60
 		PyFCGI_conf.shm.fd = shm_open(PyFCGI_conf.shm.name,
61
-			O_RDWR | O_CREAT | O_EXCL, 0770);
61
+			O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
62 62
 		if(PyFCGI_conf.shm.fd == -1)
63 63
 		{
64 64
 			err = errno;
@@ -90,6 +90,7 @@ pyfcgi_log(LOG_INFO, "MMAP LENGHt : %ld", PyFCGI_conf.shm.len);
90 90
 			pyfcgi_IPC_destroy(IPC_SHMST);
91 91
 			return -1;
92 92
 		}
93
+		memset(PyFCGI_conf.shm.ptr, 0, PyFCGI_conf.shm.len);
93 94
 	}
94 95
 	return res;
95 96
 }
@@ -125,7 +126,8 @@ int pyfcgi_IPC_init(pyfcgi_ipc_flag_t flag)
125 126
 	if(flag & IPC_SHMST)
126 127
 	{
127 128
 		flag ^= IPC_SHMST;
128
-		PyFCGI_conf.shm.fd = shm_open(PyFCGI_conf.shm.name, O_RDONLY, 0);
129
+		PyFCGI_conf.shm.fd = shm_open(PyFCGI_conf.shm.name, O_RDONLY,
130
+			S_IRUSR | S_IWUSR);
129 131
 		if(PyFCGI_conf.shm.fd == -1)
130 132
 		{
131 133
 			err = errno;

+ 59
- 0
src/responder.c View File

@@ -69,6 +69,12 @@ void init_context()
69 69
 		sleep(1);
70 70
 		clean_exit(PYFCGI_FATAL);
71 71
 	}
72
+	if(sem_post(PyFCGI_SEM(SEM_STATS).sem) < 0)
73
+	{
74
+		pyfcgi_log(LOG_ALERT, "Unable to POST stat semaphore : %s",
75
+			strerror(errno));
76
+		clean_exit(PYFCGI_FATAL);
77
+	}
72 78
 }
73 79
 
74 80
 int responder_loop()
@@ -150,6 +156,7 @@ int responder_loop()
150 156
 	{
151 157
 		pyfcgi_wd_arm();
152 158
 		PyFCGI_conf.context.n_wrk = n_wrk;
159
+		pyfcgi_pool_shm_update(n_wrk);
153 160
 		if( (ret = waitpid(0, &status, WNOHANG)) )
154 161
 		{
155 162
 			if(ret < 0)
@@ -497,3 +504,55 @@ void pool_wd_sighandler(int signum)
497 504
 	clean_exit(PYFCGI_TIMEOUT);
498 505
 	exit(PYFCGI_TIMEOUT);
499 506
 }
507
+
508
+
509
+void pyfcgi_pool_shm_update(int nworker)
510
+{
511
+	short retry;
512
+	int err;
513
+	pyfcgi_stats_shm_t *data;
514
+	struct timespec req;
515
+
516
+	req.tv_sec = 0;
517
+	req.tv_nsec = 10000000; //0.01s
518
+
519
+	retry = 0;
520
+	while(1)
521
+	{
522
+		if(sem_trywait(PyFCGI_SEM(SEM_STATS).sem) < 0)
523
+		{
524
+			err = errno;
525
+			if(err == EAGAIN)
526
+			{
527
+				if(retry >= 5)
528
+				{
529
+					pyfcgi_log(LOG_ALERT,
530
+						"Deadlock on SEM_STATS");
531
+						clean_exit(PYFCGI_FATAL);
532
+				}
533
+				nanosleep(&req, NULL);
534
+				continue;
535
+			}
536
+			pyfcgi_log(LOG_ALERT,
537
+				"Unable to wait stats semaphore : %s",
538
+				strerror(err));
539
+			clean_exit(PYFCGI_FATAL);
540
+		}
541
+		break;
542
+	}
543
+
544
+	data = (pyfcgi_stats_shm_t*)PyFCGI_conf.shm.ptr;
545
+	data->nworker = nworker;
546
+	if(sem_getvalue(PyFCGI_SEM(SEM_WSTATE).sem, &(data->pool_load)) < 0)
547
+	{
548
+		data->pool_load = -1;
549
+	}
550
+
551
+	if(sem_post(PyFCGI_SEM(SEM_STATS).sem) < 0)
552
+	{
553
+		pyfcgi_log(LOG_ALERT, "Unable to post sem at shm update : %s",
554
+			strerror(errno));
555
+		clean_exit(PYFCGI_FATAL);
556
+	}
557
+}
558
+

+ 98
- 8
src/stats.c View File

@@ -2,6 +2,12 @@
2 2
 
3 3
 static pyfcgi_stats_t pyfcgi_stats;
4 4
 
5
+static void pyfcgi_stats_add_sample(pyfcgi_stats_sample_t* samples, int sample)
6
+{
7
+	samples->samples[samples->cur] = sample;
8
+	samples->cur++;
9
+}
10
+
5 11
 int pyfcgi_stats_init()
6 12
 {
7 13
 	struct sigaction act;
@@ -54,18 +60,26 @@ void pyfcgi_stats_collector(int signum)
54 60
 {
55 61
 	int ret;
56 62
 	pyfcgi_stats_t *stats;
63
+	pyfcgi_stats_shm_t shm_data;
57 64
 
58 65
 	stats = &pyfcgi_stats;
59 66
 
60
-	stats->reqs[stats->cur_req] = 0;
67
+	stats->reqs.samples[stats->reqs.cur] = 0;
61 68
 	while( !(ret = sem_trywait(PyFCGI_SEM(SEM_WREQS).sem)) )
62 69
 	{
63
-		stats->reqs[stats->cur_req]++;
70
+		stats->reqs.samples[stats->reqs.cur]++;
71
+	}
72
+	stats->reqs.cur++;
73
+	stats->reqs.cur %= PYFCGI_STATS_SZ;
74
+
75
+	if(pyfcgi_stats_get_shm(&shm_data) < 0)
76
+	{
77
+		kill(getpid(), SIGTERM);
64 78
 	}
65
-	pyfcgi_log(LOG_DEBUG, "s#%d %d req/s", pyfcgi_stats.cur_req,
66
-		pyfcgi_stats.reqs[stats->cur_req]);
67
-	stats->cur_req++;
68
-	stats->cur_req %= PYFCGI_STATS_SZ;
79
+
80
+	pyfcgi_stats_add_sample(&(stats->wcount), shm_data.nworker);
81
+	pyfcgi_stats_add_sample(&(stats->load),
82
+		shm_data.nworker - shm_data.pool_load);
69 83
 
70 84
 	return;
71 85
 }
@@ -113,11 +127,37 @@ size_t pyfcgi_stats_format()
113 127
 
114 128
 	do
115 129
 	{
116
-		ret = pyfcgi_stats_avg(pyfcgi_stats.reqs,
117
-			&(pyfcgi_stats.cur_req), &last_rs, avgs);
130
+		last_rs = pyfcgi_stats.reqs.cur;
131
+		ret = pyfcgi_stats_avg(pyfcgi_stats.reqs.samples,
132
+			&(pyfcgi_stats.reqs.cur), &last_rs, avgs);
118 133
 	} while(ret < 0 && errno == EINTR);
134
+
119 135
 	pyfcgi_stats_buffprintf("Requests stats :\n1s:%dr/s 1m:%.2fr/s 5m:%.2fr/s 10m:%.2fr/s 15m:%.2fr/s\n",
120 136
 		last_rs, avgs[0], avgs[1], avgs[2], avgs[3]);
137
+
138
+	// Worker counter stats formating
139
+	do
140
+	{
141
+		last_rs = pyfcgi_stats.wcount.cur;
142
+		pyfcgi_stats_avg(pyfcgi_stats.wcount.samples,&(pyfcgi_stats.wcount.cur),
143
+			&(last_rs), avgs);
144
+	} while(ret < 0 && errno == EINTR);
145
+
146
+	pyfcgi_stats_buffprintf("Worker count :\n1s:%d 1m:%.2f 5m:%.2f 10m:%.2f 15m:%.2f\n",
147
+		last_rs, avgs[0], avgs[1], avgs[2], avgs[3]);
148
+
149
+	// Load stats formating
150
+	do
151
+	{
152
+		last_rs = pyfcgi_stats.load.cur;
153
+		pyfcgi_stats_avg(pyfcgi_stats.load.samples,&(pyfcgi_stats.load.cur),
154
+			&(last_rs), avgs);
155
+	} while(ret < 0 && errno == EINTR);
156
+
157
+	pyfcgi_stats_buffprintf("Load average :\n1s:%d 1m:%.2f 5m:%.2f 10m:%.2f 15m:%.2f\n",
158
+		last_rs, avgs[0], avgs[1], avgs[2], avgs[3]);
159
+
160
+
121 161
 	return pyfcgi_stats.buff_ptr;
122 162
 }
123 163
 
@@ -155,6 +195,7 @@ int pyfcgi_stats_avg(const int data[PYFCGI_STATS_SZ], int *idx_nxt, int *last,
155 195
 		if(i<=15*60) { r15 += rtmp; }
156 196
 		stmp = cur;
157 197
 	}
198
+	r15 += (long double)stmp / 60;
158 199
 	//Restore interrupt/ALARM ??
159 200
 	r5 /= 5;
160 201
 	r10 /= 10;
@@ -273,3 +314,52 @@ const char *pyfcgi_stats_buff(const char **buff, size_t* len)
273 314
 	*len = pyfcgi_stats.buff_ptr;
274 315
 	return *buff;
275 316
 }
317
+
318
+
319
+int pyfcgi_stats_get_shm(pyfcgi_stats_shm_t *res)
320
+{
321
+	short retry;
322
+	int err;
323
+	struct timespec req;
324
+
325
+	req.tv_sec = 0;
326
+	req.tv_nsec = 10000000; //0.01s
327
+
328
+	retry = 0;
329
+
330
+	while(1)
331
+	{
332
+		if(sem_trywait(PyFCGI_SEM(SEM_STATS).sem) < 0)
333
+		{
334
+			err = errno;
335
+			if(err == EAGAIN)
336
+			{
337
+				if(retry >= 5)
338
+				{
339
+					pyfcgi_log(LOG_ALERT,
340
+						"Deadlock on SEM_STATS");
341
+					return -1;
342
+				}
343
+				nanosleep(&req, NULL);
344
+				continue;
345
+			}
346
+			pyfcgi_log(LOG_ALERT,
347
+				"Unable to wait stats semaphore : %s",
348
+				strerror(err));
349
+			return -1;
350
+		}
351
+		break;
352
+	}
353
+
354
+	*res = *(pyfcgi_stats_shm_t*)PyFCGI_conf.shm.ptr;
355
+
356
+	if(sem_post(PyFCGI_SEM(SEM_STATS).sem) < 0)
357
+	{
358
+		pyfcgi_log(LOG_ALERT, "Unable to post sem at shm update : %s",
359
+			strerror(errno));
360
+		return -1;
361
+	}
362
+	return 0;
363
+}
364
+
365
+

Loading…
Cancel
Save