ColdFusion Database Pools and Resource Counting
With a discussion with Sammy he suggested that my ColdFusion database pool DSN component could be extended to select the current datasource with the least no of connections rather than just randomly picking one.
Here's the modified code.
DSN.cfc:
1<cfcomponent displayname="DSN" extends="singleton">
2 <cfset variables.DSN = arraynew(1)>
3 <cfset variables.DSNs = "">
4 <cfset variables.length = 0>
5
6 <cffunction name="getDSN" access="public" returntype="string" output="true">
7 <cfscript>
8 var min = 0;
9 var use = 0;
10 var i = 0;
11 for (i=1; i lte variables.length; i=i+1) {
12 if (variables.DSN[i].count lte min) {
13 use = i;
14 min = variables.DSN[i].count;
15
16 }
17 }
18 variables.DSN[use].count = variables.DSN[use].count + 1;
19 return variables.DSN[use].DSN;
20 </cfscript>
21 </cffunction>
22
23 <cffunction name="freeDSN" access="public" output="false">
24 <cfargument name="DSN" type="string" required="yes">
25
26 <cfscript>
27 var i = 0;
28 for (i=1; i lt variables.length; i=i+1) {
29 if (variables.DSN[i].DSN is arguments.DSN) {
30 variables.DSN[i].count = variables.DSN[i].count - 1;
31 break;
32 }
33 }
34 return;
35 </cfscript>
36 </cffunction>
37
38 <cffunction name="getAllDSNs" access="public" returntype="string" output="false">
39 <cfreturn variables.DSNs>
40 </cffunction>
41
42 <cffunction name="setDSNs" access="public" output="false">
43 <cfargument name="DSNs" type="string" required="yes">
44
45 <cfscript>
46 var i = 0;
47 for (i=1; i lte listlen(DSNs); i=i+1) {
48 variables.DSN[i] = structnew();
49 variables.DSN[i].DSN = listgetat(DSNs,i);
50 variables.DSN[i].count = 0;
51 }
52 variables.DSNs = DSNs;
53 variables.length = listlen(arguments.DSNs);
54 </cfscript>
55 </cffunction>
56</cfcomponent>
2 <cfset variables.DSN = arraynew(1)>
3 <cfset variables.DSNs = "">
4 <cfset variables.length = 0>
5
6 <cffunction name="getDSN" access="public" returntype="string" output="true">
7 <cfscript>
8 var min = 0;
9 var use = 0;
10 var i = 0;
11 for (i=1; i lte variables.length; i=i+1) {
12 if (variables.DSN[i].count lte min) {
13 use = i;
14 min = variables.DSN[i].count;
15
16 }
17 }
18 variables.DSN[use].count = variables.DSN[use].count + 1;
19 return variables.DSN[use].DSN;
20 </cfscript>
21 </cffunction>
22
23 <cffunction name="freeDSN" access="public" output="false">
24 <cfargument name="DSN" type="string" required="yes">
25
26 <cfscript>
27 var i = 0;
28 for (i=1; i lt variables.length; i=i+1) {
29 if (variables.DSN[i].DSN is arguments.DSN) {
30 variables.DSN[i].count = variables.DSN[i].count - 1;
31 break;
32 }
33 }
34 return;
35 </cfscript>
36 </cffunction>
37
38 <cffunction name="getAllDSNs" access="public" returntype="string" output="false">
39 <cfreturn variables.DSNs>
40 </cffunction>
41
42 <cffunction name="setDSNs" access="public" output="false">
43 <cfargument name="DSNs" type="string" required="yes">
44
45 <cfscript>
46 var i = 0;
47 for (i=1; i lte listlen(DSNs); i=i+1) {
48 variables.DSN[i] = structnew();
49 variables.DSN[i].DSN = listgetat(DSNs,i);
50 variables.DSN[i].count = 0;
51 }
52 variables.DSNs = DSNs;
53 variables.length = listlen(arguments.DSNs);
54 </cfscript>
55 </cffunction>
56</cfcomponent>
Using the component is exactly the same except you have to call freeDSN after the query is run. So the calling code becomes:
1<cfset dsn = getInstance("dsn")>
2<cfset currentdsn = dsn.getDSN()>
3
4<cfquery name="myquery1" datasource="#currentdsn#">
5select ....
6</cfquery>
7<cfset dsn.freeDNS(currentdsn)>
2<cfset currentdsn = dsn.getDSN()>
3
4<cfquery name="myquery1" datasource="#currentdsn#">
5select ....
6</cfquery>
7<cfset dsn.freeDNS(currentdsn)>
Alternatively you could create a custom tag dsn.cfm
1<cfif thisTag.executionMode IS "start">
2 <cfset request.currentdsn = caller.dsn.getDSN()>
3<cfelseif NOT thisTag.hasEndTag>
4 <cfthrow message="Missing end tag.">
5<cfelseif thisTag.executionMode IS "end">
6 <cfset caller.dsn.freeDSN(request.currentdsn)>
7</cfif>
2 <cfset request.currentdsn = caller.dsn.getDSN()>
3<cfelseif NOT thisTag.hasEndTag>
4 <cfthrow message="Missing end tag.">
5<cfelseif thisTag.executionMode IS "end">
6 <cfset caller.dsn.freeDSN(request.currentdsn)>
7</cfif>
And call it like so:
1<cfset dsn = getInstance("dsn")>
2<cf_dsn>
3<cfquery name="myquery1" datasource="#request.currentdsn#">
4select ....
5</cfquery>
6</cf_dsn>
2<cf_dsn>
3<cfquery name="myquery1" datasource="#request.currentdsn#">
4select ....
5</cfquery>
6</cf_dsn>
TweetBacks
With the master/slave(s) setup I assume you have the data replication at the database level rather than at the application level (like my code). Only down side I see if that if the master db goes off offline you can no longer do any update queries but it would generally be easier to keep the databases in sync.