1
/* $Id: ncbi_service_dispd.c,v 6.34 2001/12/04 15:57:05 lavr Exp $
2
* ===========================================================================
5
* National Center for Biotechnology Information
7
* This software/database is a "United States Government Work" under the
8
* terms of the United States Copyright Act. It was written as part of
9
* the author's official duties as a United States Government employee and
10
* thus cannot be copyrighted. This software/database is freely available
11
* to the public for use. The National Library of Medicine and the U.S.
12
* Government have not placed any restriction on its use or reproduction.
14
* Although all reasonable efforts have been taken to ensure the accuracy
15
* and reliability of the software and data, the NLM and the U.S.
16
* Government do not and cannot warrant the performance or results that
17
* may be obtained by using this software or data. The NLM and the U.S.
18
* Government disclaim all warranties, express or implied, including
19
* warranties of performance, merchantability or fitness for any particular
22
* Please cite the author in any work or product based on this material.
24
* ===========================================================================
26
* Author: Anton Lavrentiev
29
* Low-level API to resolve NCBI service name to the server meta-address
30
* with the use of NCBI network dispatcher (DISPD).
32
* --------------------------------------------------------------------------
33
* $Log: ncbi_service_dispd.c,v $
34
* Revision 6.34 2001/12/04 15:57:05 lavr
35
* Change log correction
37
* Revision 6.33 2001/10/01 19:53:39 lavr
38
* -s_FreeData(), -s_ResetData() - do everything in s_Close()/s_Reset() instead
40
* Revision 6.32 2001/09/29 19:33:04 lavr
41
* BUGFIX: SERV_Update() requires VT bound (was not the case in constructor)
43
* Revision 6.31 2001/09/29 18:41:03 lavr
44
* "Server-Keyed-Info:" removed from protocol
46
* Revision 6.30 2001/09/28 20:52:16 lavr
47
* Update VT method revised as now called on a per-line basis
49
* Revision 6.29 2001/09/24 20:30:01 lavr
50
* Reset() VT method added and utilized
52
* Revision 6.28 2001/09/10 21:23:53 lavr
53
* "Relay-Mode:" tag eliminated from the dispatcher protocol
55
* Revision 6.27 2001/07/24 18:02:02 lavr
56
* Seed random generator at Open()
58
* Revision 6.26 2001/07/18 17:41:25 lavr
59
* BUGFIX: In code for selecting services by preferred host
61
* Revision 6.25 2001/07/03 20:49:44 lavr
62
* RAND_MAX included in the interval search
64
* Revision 6.24 2001/06/25 15:36:38 lavr
65
* s_GetNextInfo now takes one additional argument for host environment
67
* Revision 6.23 2001/06/20 17:27:49 kans
68
* include <time.h> for Mac compiler
70
* Revision 6.22 2001/06/19 19:12:01 lavr
71
* Type change: size_t -> TNCBI_Size; time_t -> TNCBI_Time
73
* Revision 6.21 2001/05/17 15:02:51 lavr
76
* Revision 6.20 2001/05/11 15:30:31 lavr
77
* Protocol change: REQUEST_FAILED -> DISP_FAILURES
79
* Revision 6.19 2001/05/03 16:58:16 lavr
80
* FIX: Percent is taken of local bonus coef instead of the value itself
82
* Revision 6.18 2001/05/03 16:35:53 lavr
83
* Local bonus coefficient modified: meaning of negative value changed
85
* Revision 6.17 2001/04/26 20:20:01 lavr
86
* Better way of choosing local server with a tiny (e.g. penalized) status
88
* Revision 6.16 2001/04/24 21:35:46 lavr
89
* Treatment of new bonus coefficient for local servers
91
* Revision 6.15 2001/03/21 21:24:11 lavr
92
* Type match (int) for %n in scanf
94
* Revision 6.14 2001/03/06 23:57:27 lavr
95
* SERV_DISPD_LOCAL_SVC_BONUS used for services running locally
97
* Revision 6.13 2001/03/05 23:10:46 lavr
98
* SERV_ReadInfo takes only one argument now
100
* Revision 6.12 2001/03/01 00:33:12 lavr
101
* FIXES: Empty update does not generate parse error
102
* Dispathing error is only logged in debug mode; milder severity
104
* Revision 6.11 2001/02/09 17:36:48 lavr
105
* Modified: fSERV_StatelessOnly overrides info->stateless
107
* Revision 6.10 2001/01/25 17:06:36 lavr
108
* s_FreeData now calls ConnNetInfo_Destroy() unconditionally
110
* Revision 6.9 2001/01/12 23:51:40 lavr
111
* Message logging modified for use LOG facility only
113
* Revision 6.8 2001/01/08 23:48:14 lavr
114
* (unsigned char) conversion in isspace
116
* Revision 6.7 2001/01/08 22:40:23 lavr
117
* Further development of service-mapping protocol: stateless/stateful
118
* is now separated from firewall/direct mode (see also in few more files)
120
* Revision 6.6 2000/12/29 18:05:46 lavr
121
* First working revision.
123
* Revision 6.5 2000/10/20 17:36:05 lavr
124
* Partially working dispd dispatcher client (service mapping works)
125
* Checkin for backup purposes; working code '#if 0'-ed out
127
* Revision 6.4 2000/10/05 22:43:30 lavr
128
* Another dummy revision: still in development
130
* Revision 6.3 2000/10/05 22:34:23 lavr
131
* Temporary (dummy) revision for compilation to go
133
* Revision 6.2 2000/05/22 16:53:12 lavr
134
* Rename service_info -> server_info everywhere (including
135
* file names) as the latter name is more relevant
137
* Revision 6.1 2000/05/12 18:43:59 lavr
140
* ==========================================================================
143
#include "ncbi_comm.h"
144
#if defined(_DEBUG) && !defined(NDEBUG)
145
#include "ncbi_priv.h"
147
#include "ncbi_servicep_dispd.h"
148
#include <connect/ncbi_ansi_ext.h>
149
#include <connect/ncbi_connection.h>
150
#include <connect/ncbi_http_connector.h>
157
/* Lower bound of up-to-date/out-of-date ratio */
158
#define SERV_DISPD_STALE_RATIO_OK 0.8
159
/* Default rate increase if svc runs locally */
160
#define SERV_DISPD_LOCAL_SVC_BONUS 1.2
166
static void s_Reset(SERV_ITER);
167
static SSERV_Info* s_GetNextInfo(SERV_ITER, char**);
168
static int/*bool*/ s_Update(SERV_ITER, TNCBI_Time, const char*);
169
static void s_Close(SERV_ITER);
171
static const SSERV_VTable s_op = {
172
s_Reset, s_GetNextInfo, s_Update, 0, s_Close, "DISPD"
179
static int s_RandomSeed = 0;
189
SConnNetInfo* net_info;
196
static int/*bool*/ s_AddServerInfo(SDISPD_Data* data, SSERV_Info* info)
200
/* First check that the new server info is updating existing one */
201
for (i = 0; i < data->n_node; i++) {
202
if (SERV_EqualInfo(data->s_node[i].info, info)) {
203
/* Replace older version */
204
free(data->s_node[i].info);
205
data->s_node[i].info = info;
210
/* Next, add new service to the list */
211
if (data->n_node == data->n_max_node) {
212
size_t n = data->n_max_node + 10;
216
temp = (SDISPD_Node*) realloc(data->s_node, sizeof(*temp) * n);
218
temp = (SDISPD_Node*) malloc(sizeof(*temp) * n);
223
data->n_max_node = n;
226
data->s_node[data->n_node++].info = info;
233
static int s_ParseHeader(const char*, void*, int);
235
#endif /* __cplusplus */
237
static int/*bool*/ s_ParseHeader(const char* header, void *data,
238
int/*bool, ignored*/ server_error)
240
SERV_Update((SERV_ITER) data, header);
241
return 1/*header parsed okay*/;
245
static int/*bool*/ s_Resolve(SERV_ITER iter)
247
static const char service[] = "service=";
248
static const char stateless[] = "Client-Mode: STATELESS_ONLY\r\n";
249
static const char dispatch_mode[] = "Dispatch-Mode: INFORMATION_ONLY\r\n";
250
static const char stateful_capable[] = "Client-Mode: STATEFUL_CAPABLE\r\n";
251
SConnNetInfo *net_info = ((SDISPD_Data*) iter->data)->net_info;
259
/* Form service name argument (as CGI argument) */
260
if (strlen(iter->service) + sizeof(service) > sizeof(net_info->args))
262
strcpy(net_info->args, service);
263
strcat(net_info->args, iter->service);
264
/* Reset request method to be GET (as no HTTP body will follow) */
265
net_info->req_method = eReqMethod_Get;
266
/* Obtain additional header information */
267
if ((s = SERV_Print(iter)) != 0) {
268
int status = BUF_Write(&buf, s, strlen(s));
275
tag = net_info->stateless ? stateless : stateful_capable;
276
if (!BUF_Write(&buf, tag, strlen(tag)) ||
277
!BUF_Write(&buf, dispatch_mode, sizeof(dispatch_mode)-1)) {
281
/* Now the entire user header is ready, take it out of the buffer */
282
buflen = BUF_Size(buf);
284
if ((s = (char*) malloc(buflen + 1)) != 0) {
285
if (BUF_Read(buf, s, buflen) != buflen) {
294
ConnNetInfo_SetUserHeader(net_info, s);
295
assert(strcmp(net_info->http_user_header, s) == 0);
297
/* All the rest in the net_info structure is fine with us */
298
if (!(c = HTTP_CreateConnectorEx(net_info, 0/*flags*/, s_ParseHeader,
299
0/*adj.info*/, iter/*data*/, 0/*clnup*/)))
301
if (CONN_Create(c, &conn) != eIO_Success)
303
/* This dummy read will send all the HTTP data, we'll get a callback */
304
CONN_Read(conn, 0, 0, &buflen, eIO_Plain);
306
return ((SDISPD_Data*) iter->data)->n_node != 0;
310
static int/*bool*/ s_Update(SERV_ITER iter, TNCBI_Time now, const char* text)
312
static const char server_info[] = "Server-Info-";
313
SDISPD_Data* data = (SDISPD_Data*) iter->data;
315
if (strncasecmp(text, server_info, sizeof(server_info) - 1) == 0) {
316
const char* p = text + sizeof(server_info) - 1;
321
if (sscanf(p, "%u: %n", &d1, &d2) < 1)
322
return 0/*not updated*/;
323
if ((info = SERV_ReadInfo(p + d2)) != 0) {
324
info->time += now; /* expiration time now */
325
if (s_AddServerInfo(data, info))
329
} else if (strncasecmp(text, HTTP_DISP_FAILURES,
330
sizeof(HTTP_DISP_FAILURES) - 1) == 0) {
331
#if defined(_DEBUG) && !defined(NDEBUG)
332
const char* p = text + sizeof(HTTP_DISP_FAILURES) - 1;
333
while (*p && isspace((unsigned char)(*p)))
335
if (data->net_info->debug_printout)
336
CORE_LOGF(eLOG_Warning, ("[DISPATCHER] %s", p));
341
return 0/*not updated*/;
345
static int/*bool*/ s_IsUpdateNeeded(SDISPD_Data *data)
347
double status = 0.0, total = 0.0;
348
TNCBI_Time t = (TNCBI_Time) time(0);
351
while (i < data->n_node) {
352
SSERV_Info* info = data->s_node[i].info;
355
if (info->time < t) {
356
if (i < --data->n_node)
357
memmove(data->s_node + i, data->s_node + i + 1,
358
(data->n_node - i)*sizeof(*data->s_node));
361
status += info->rate;
365
return total != 0.0 ? (status/total < SERV_DISPD_STALE_RATIO_OK) : 1;
369
static SSERV_Info* s_GetNextInfo(SERV_ITER iter, char** env)
371
double total = 0.0, point = -1.0, access = 0.0, p = 0.0, status;
372
SDISPD_Data* data = (SDISPD_Data*) iter->data;
379
if (s_IsUpdateNeeded(data) && !s_Resolve(iter))
381
assert(data->n_node != 0);
383
for (i = 0; i < data->n_node; i++) {
384
info = data->s_node[i].info;
386
assert(status != 0.0);
388
if (info->host == iter->preferred_host) {
389
if (info->coef <= 0.0) {
390
status *= SERV_DISPD_LOCAL_SVC_BONUS;
391
if (info->coef < 0.0 && access < status) {
393
point = total + status; /* Latch this local server */
397
status *= info->coef;
400
data->s_node[i].status = total;
403
/* We will take pre-chosen local server only if its status is not less
404
than p% of the average rest status; otherwise, we ignore the server,
405
and apply the general procedure by seeding a random point. */
406
if (point < 0.0 || access*(data->n_node - 1) < p*0.01*(total - access))
407
point = (total * rand()) / (double) RAND_MAX;
408
for (i = 0; i < data->n_node; i++) {
409
if (point <= data->s_node[i].status)
412
assert(i < data->n_node);
414
info = data->s_node[i].info;
415
info->rate = data->s_node[i].status - (i ? data->s_node[i-1].status : 0.0);
416
if (i < --data->n_node) {
417
memmove(data->s_node + i, data->s_node + i + 1,
418
(data->n_node - i)*sizeof(*data->s_node));
427
static void s_Reset(SERV_ITER iter)
429
SDISPD_Data* data = (SDISPD_Data*) iter->data;
430
if (data && data->s_node) {
432
assert(data->n_max_node);
433
for (i = 0; i < data->n_node; i++)
434
free(data->s_node[i].info);
440
static void s_Close(SERV_ITER iter)
442
SDISPD_Data* data = (SDISPD_Data*) iter->data;
443
assert(data->n_node == 0); /* s_Reset() had to be called before */
446
ConnNetInfo_Destroy(data->net_info);
452
/***********************************************************************
454
***********************************************************************/
456
const SSERV_VTable* SERV_DISPD_Open(SERV_ITER iter,
457
const SConnNetInfo* net_info)
461
if (!(data = (SDISPD_Data*) malloc(sizeof(*data))))
464
s_RandomSeed = (int)time(0) + (int)SOCK_gethostbyname(0);
467
data->net_info = ConnNetInfo_Clone(net_info);
468
if (iter->type & fSERV_StatelessOnly)
469
data->net_info->stateless = 1/*true*/;
470
if (iter->type & fSERV_Firewall)
471
data->net_info->firewall = 1/*true*/;
472
data->n_node = data->n_max_node = 0;
476
iter->op = &s_op; /* SERV_Update() - from HTTP callback - expects this */
477
if (!s_Resolve(iter)) {