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