1 /******************************************************************************
2  *
3  * Project:  MapServer
4  * Purpose:  MapCache connection pooling
5  * Author:   Thomas Bonfort and the MapServer team.
6  *
7  ******************************************************************************
8  * Copyright (c) 1996-2011 Regents of the University of Minnesota.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies of this Software or works derived from this Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  *****************************************************************************/
28 
29 #include <apr_reslist.h>
30 #include "mapcache.h"
31 
32 struct mapcache_connection_pool {
33     apr_pool_t *server_pool;
34     apr_reslist_t *connexions;
35 };
36 
37 
38 struct mapcache_pooled_connection_container {
39   mapcache_pooled_connection *head;
40   apr_pool_t *pool;
41   unsigned int max_list_size;
42 };
43 
44 
45 
46 struct mapcache_pooled_connection_private_data {
47   char *key;
48   mapcache_connection_destructor destructor;
49   mapcache_pooled_connection *next;
50   mapcache_pooled_connection_container *pcc;
51 };
52 
mapcache_connection_container_creator(void ** conn_,void * params,apr_pool_t * pool)53 static apr_status_t mapcache_connection_container_creator(void **conn_, void *params, apr_pool_t *pool) {
54   mapcache_pooled_connection_container *pcc;
55   pcc = calloc(1, sizeof(mapcache_pooled_connection_container));
56   pcc->max_list_size = 10;
57   pcc->pool = pool;
58   *conn_ = pcc;
59   return APR_SUCCESS;
60 }
61 
mapcache_connection_container_destructor(void * conn_,void * params,apr_pool_t * pool)62 static apr_status_t mapcache_connection_container_destructor(void *conn_, void *params, apr_pool_t *pool) {
63   mapcache_pooled_connection_container *pcc = (mapcache_pooled_connection_container*)conn_;
64   mapcache_pooled_connection *pc = pcc->head;
65   while(pc) {
66     mapcache_pooled_connection *this = pc;
67     this->private->destructor(this->connection);
68     free(this->private->key);
69     pc = this->private->next;
70     free(this);
71   }
72   free(pcc);
73   return MAPCACHE_SUCCESS;
74 }
75 
76 
mapcache_connection_pool_create(mapcache_cfg * cfg,mapcache_connection_pool ** cp,apr_pool_t * server_pool)77 apr_status_t mapcache_connection_pool_create(mapcache_cfg *cfg, mapcache_connection_pool **cp, apr_pool_t *server_pool) {
78   apr_status_t rv;
79   *cp = apr_pcalloc(server_pool, sizeof(mapcache_connection_pool));
80   (*cp)->server_pool = server_pool;
81   rv = apr_reslist_create(&((*cp)->connexions), 1, 5, cfg->cp_hmax, cfg->cp_ttl,
82       mapcache_connection_container_creator,
83       mapcache_connection_container_destructor,
84       NULL,
85       server_pool);
86   return rv;
87 }
88 
mapcache_connection_pool_get_connection(mapcache_context * ctx,char * key,mapcache_connection_constructor constructor,mapcache_connection_destructor destructor,void * params)89 mapcache_pooled_connection* mapcache_connection_pool_get_connection(mapcache_context *ctx, char *key,
90         mapcache_connection_constructor constructor, mapcache_connection_destructor destructor,
91         void *params) {
92   apr_status_t rv;
93   int count = 0;
94   mapcache_pooled_connection_container *pcc;
95   mapcache_pooled_connection *pc,*pred=NULL;
96   rv = apr_reslist_acquire(ctx->connection_pool->connexions, (void**)&pcc);
97   if(rv != APR_SUCCESS || !pcc) {
98     char errmsg[120];
99     ctx->set_error(ctx,500, "failed to acquire connection from mapcache connection pool: (%s)", apr_strerror(rv, errmsg,120));
100     return NULL;
101   }
102 
103   /* loop through existing connections to see if we find one matching the given key */
104   pc = pcc->head;
105   while(pc) {
106     count++;
107     if(!strcmp(key,pc->private->key)) {
108       /* move current connection to head of list, and return it. We only move the connection
109          to the front of the list if it wasn't in the first 2 connections, as in the seeding
110          case we are always alternating between read and write operations (i.e. potentially
111          2 different connections and in that cas we end up switching connections each time
112          there's an access */
113       if(pc != pcc->head && count>2) {
114         assert(pred);
115         pred->private->next = pc->private->next;
116         pc->private->next = pcc->head;
117         pcc->head = pc;
118       }
119       return pc;
120     }
121     pred = pc;
122     pc = pc->private->next;
123   }
124 
125   /* connection not found in pool */
126   pc = calloc(1,sizeof(mapcache_pooled_connection));
127   /*
128   ctx->log(ctx, MAPCACHE_DEBUG, "calling constructor for pooled connection (%s)", key);
129   */
130   constructor(ctx, &pc->connection, params);
131   if(GC_HAS_ERROR(ctx)) {
132     free(pc);
133     apr_reslist_release(ctx->connection_pool->connexions, pcc);
134     return NULL;
135   }
136 
137   pc->private = calloc(1,sizeof(mapcache_pooled_connection_private_data));
138   pc->private->key = strdup(key);
139   pc->private->destructor = destructor;
140   pc->private->next = pcc->head;
141   pc->private->pcc = pcc;
142 
143   if(count == pcc->max_list_size) {
144     /* max number of connections atained, we must destroy the last one that was used */
145     mapcache_pooled_connection *opc;
146     opc = pcc->head;
147     count = 1;
148     while(count < pcc->max_list_size) {
149       pred = opc;
150       opc = opc->private->next;
151       count++;
152     }
153     ctx->log(ctx, MAPCACHE_DEBUG, "tearing down pooled connection (%s) to make room", opc->private->key);
154     opc->private->destructor(opc->connection);
155     free(opc->private->key);
156     free(opc->private);
157     free(opc);
158     if(pred) {
159       pred->private->next = NULL;
160     }
161   }
162   pcc->head = pc;
163   return pc;
164 
165 }
mapcache_connection_pool_invalidate_connection(mapcache_context * ctx,mapcache_pooled_connection * connection)166 void mapcache_connection_pool_invalidate_connection(mapcache_context *ctx, mapcache_pooled_connection *connection) {
167   mapcache_pooled_connection_container *pcc = connection->private->pcc;
168   mapcache_pooled_connection *pc = pcc->head, *pred=NULL;
169   while(pc) {
170     if(pc == connection) {
171       if(pred) {
172         pred->private->next = pc->private->next;
173       } else {
174         pcc->head = pc->private->next;
175       }
176       pc->private->destructor(pc->connection);
177       free(pc->private->key);
178       free(pc);
179       break;
180     }
181     pred = pc;
182     pc = pc->private->next;
183   }
184   apr_reslist_release(ctx->connection_pool->connexions,(void*)pcc);
185 }
186 
mapcache_connection_pool_release_connection(mapcache_context * ctx,mapcache_pooled_connection * connection)187 void mapcache_connection_pool_release_connection(mapcache_context *ctx, mapcache_pooled_connection *connection) {
188   if(connection) {
189     mapcache_pooled_connection_container *pcc = connection->private->pcc;
190     apr_reslist_release(ctx->connection_pool->connexions,(void*)pcc);
191   }
192 }
193 
194