We have just installed the latest SQL 13 drivers and the latest sqlsrv and sqlsrv_pdo PHP extensions. Connection pooling does not seem to be working.
Summary of test: We created a test script which will connect to the DB. After the script executes the connection is closed, confirmed by the DB.
odbcinst.ini:
[ODBC]
Pooling = Yes
[ODBC Driver 13 for SQL Server]
Description=Microsoft ODBC Driver 13 for SQL Server
Driver=/opt/microsoft/msodbcsql/lib64/libmsodbcsql-13.0.so.1.0
UsageCount=1
CPTimeout=120
Test script which includes some timing:
$startTime = microtime(true);
function printElapsedTimeSinceLastEvent($event=''){
global $startTime;
$elapsed = microtime(true) - $startTime;
echo $event . round($elapsed*1000) ."\n";
$startTime = microtime(true);
}
// Connection sqlsrv driver
$pdo = new PDO ("sqlsrv:server=10.12.12.123;database=aDatabaseName",'coolUser','someAwesomePassword');
printElapsedTimeSinceLastEvent("Connected: ");
$stmt = $pdo->prepare("SELECT TOP 10 * FROM users");
$stmt->execute();
printElapsedTimeSinceLastEvent("Execute: ");
$array = $stmt->fetchAll(PDO::FETCH_ASSOC);
printElapsedTimeSinceLastEvent("Fetch: ");
3 consecutive outputs of script:
Connected: 149
Execute: 109
Fetch: 2
Connected: 149
Execute: 100
Fetch: 2
md5-ea55919f54ff6e7b74ef307ff1bc34da
Connected: 152
Execute: 108
Fetch: 2
Using SQL Studio and running EXEC sp_who2 we can see the connections are closed after each run. Using sqlsrv on Windows does keep connections open as expected and does show up in sp_who2 output.
On Windows where we confirmed connection pooling does exist the connection time is significantly faster as one would expect with connection pooling.
Here is the trace for a simpler connection script
$pdo = new PDO ("sqlsrv:server=10.12.12.123;database=someDatabase",'someUser','somePassword');
Please note the SQLDisconnect at the end
[ODBC][9078][1479946562.696384][__handles.c][460]
Exit:[SQL_SUCCESS]
Environment = 0x7fe74a5d8f20
[ODBC][9078][1479946562.696669][SQLSetEnvAttr.c][189]
Entry:
Environment = 0x7fe74a5d8f20
Attribute = SQL_ATTR_ODBC_VERSION
Value = 0x3
StrLen = -6
[ODBC][9078][1479946562.696761][SQLSetEnvAttr.c][363]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.696782][SQLSetEnvAttr.c][189]
Entry:
Environment = 0x7fe74a5d8f20
Attribute = SQL_ATTR_CONNECTION_POOLING
Value = (nil)
StrLen = -5
[ODBC][9078][1479946562.696802][SQLSetEnvAttr.c][363]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.696813][__handles.c][460]
Exit:[SQL_SUCCESS]
Environment = 0x7fe74a5d96a0
[ODBC][9078][1479946562.696824][SQLSetEnvAttr.c][189]
Entry:
Environment = 0x7fe74a5d96a0
Attribute = SQL_ATTR_ODBC_VERSION
Value = 0x3
StrLen = -6
[ODBC][9078][1479946562.696832][SQLSetEnvAttr.c][363]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.696850][SQLSetEnvAttr.c][189]
Entry:
Environment = 0x7fe74a5d96a0
Attribute = SQL_ATTR_CONNECTION_POOLING
Value = 0x2
StrLen = -5
[ODBC][9078][1479946562.696858][SQLSetEnvAttr.c][363]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.697203][__handles.c][460]
Exit:[SQL_SUCCESS]
Environment = 0x7fe74a60da60
[ODBC][9078][1479946562.697219][SQLSetEnvAttr.c][189]
Entry:
Environment = 0x7fe74a60da60
Attribute = SQL_ATTR_ODBC_VERSION
Value = 0x3
StrLen = -6
[ODBC][9078][1479946562.697228][SQLSetEnvAttr.c][363]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.697237][SQLSetEnvAttr.c][189]
Entry:
Environment = 0x7fe74a60da60
Attribute = SQL_ATTR_CONNECTION_POOLING
Value = (nil)
StrLen = -5
[ODBC][9078][1479946562.697245][SQLSetEnvAttr.c][363]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.697254][__handles.c][460]
Exit:[SQL_SUCCESS]
Environment = 0x7fe74a60e090
[ODBC][9078][1479946562.697263][SQLSetEnvAttr.c][189]
Entry:
Environment = 0x7fe74a60e090
Attribute = SQL_ATTR_ODBC_VERSION
Value = 0x3
StrLen = -6
[ODBC][9078][1479946562.697271][SQLSetEnvAttr.c][363]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.697280][SQLSetEnvAttr.c][189]
Entry:
Environment = 0x7fe74a60e090
Attribute = SQL_ATTR_CONNECTION_POOLING
Value = 0x2
StrLen = -5
[ODBC][9078][1479946562.697288][SQLSetEnvAttr.c][363]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.699369][SQLAllocHandle.c][375]
Entry:
Handle Type = 2
Input Handle = 0x7fe74a5d96a0
[ODBC][9078][1479946562.699393][SQLAllocHandle.c][493]
Exit:[SQL_SUCCESS]
Output Handle = 0x7fe74a623c50
[ODBC][9078][1479946562.699520][SQLDriverConnectW.c][290]
Entry:
Connection = 0x7fe74a623c50
Window Hdl = (nil)
Str In = [Driver={ODBC Driver 13 for SQL Server};Server={10.12.12.123};UID={someUser};PWD={somePassword};Database={someDatabase};MARS_C...][length = 144]
Str Out = (nil)
Str Out Max = 0
Str Out Ptr = 0x7ffca4495dde
Completion = 0
UNICODE Using encoding ASCII 'UTF8' and UNICODE 'UTF16LE'
[ODBC][9078][1479946562.850460][SQLDriverConnectW.c][910]
Exit:[SQL_SUCCESS_WITH_INFO]
Connection Out [[NULL]]
DIAG [01000] [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Changed database context to 'someDatabase'.
DIAG [01000] [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Changed language setting to us_english.
[ODBC][9078][1479946562.850613][SQLGetDiagRecW.c][508]
Entry:
Connection = 0x7fe74a623c50
Rec Number = 1
SQLState = 0x7ffca4495bb0
Native = 0x7fe744c010a0
Message Text = 0x7ffca4495790
Buffer Length = 513
Text Len Ptr = 0x7ffca4495bbe
[ODBC][9078][1479946562.850646][SQLGetDiagRecW.c][550]
Exit:[SQL_SUCCESS]
SQLState = 01000
Native = 0x7fe744c010a0 -> 5701
Message Text = [[unixODBC][Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Changed database context to 'someDatabase'.]
[ODBC][9078][1479946562.850702][SQLGetInfo.c][554]
Entry:
Connection = 0x7fe74a623c50
Info Type = SQL_DBMS_VER (18)
Info Value = 0x7ffca4495c50
Buffer Length = 256
StrLen = 0x7ffca4495d54
[ODBC][9078][1479946562.850752][SQLGetInfo.c][617]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.850809][SQLEndTran.c][421]
Entry:
Connection = 0x7fe74a623c50
Completion Type = 1
[ODBC][9078][1479946562.850835][SQLGetInfo.c][554]
Entry:
Connection = 0x7fe74a623c50
Info Type = SQL_CURSOR_COMMIT_BEHAVIOR (23)
Info Value = 0x7fe74a6250c0
Buffer Length = 2
StrLen = 0x7ffca449800e
[ODBC][9078][1479946562.850857][SQLGetInfo.c][617]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.850877][SQLGetInfo.c][554]
Entry:
Connection = 0x7fe74a623c50
Info Type = SQL_CURSOR_ROLLBACK_BEHAVIOR (24)
Info Value = 0x7fe74a6250c2
Buffer Length = 2
StrLen = 0x7ffca449800e
[ODBC][9078][1479946562.850897][SQLGetInfo.c][617]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.850916][SQLEndTran.c][574]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.850934][SQLDisconnect.c][208]
Entry:
Connection = 0x7fe74a623c50
[ODBC][9078][1479946562.851017][SQLDisconnect.c][308]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.851055][SQLFreeHandle.c][284]
Entry:
Handle Type = 2
Input Handle = 0x7fe74a623c50
[ODBC][9078][1479946562.851076][SQLFreeHandle.c][333]
Exit:[SQL_SUCCESS]
[ODBC][9078][1479946562.851586][SQLFreeHandle.c][219]
Entry:
Handle Type = 1
Input Handle = 0x7fe74a60da60
I'm also having this same issue, any word?
@sean256 @champloo11 We are able to repro this issue and are looking into what's going on. Seems like its happening in the ODBC Driver under the hood. We will keep you posted! 馃憤
@meet-bhagdev thank you! We really thought we were doing something wrong on our end. Getting connection pooling working would be huge for performance.
Is there any ETA on a potential fix for this, or info on mitigation?
@champloo11 We don't have a fix for this yet. A potential mitigation: In your odbcinst.ini can you try mentioning Pooling=Yes (Example below). Run your script again and let us know what happens.
[ODBC Driver 13 for SQL Server]
Description=Microsoft ODBC Driver 13 for SQL Server
Driver=/opt/microsoft/msodbcsql/lib64/libmsodbcsql-13.0.so.1.0
UsageCount=1
Pooling = Yes
@meet-bhagdev It was my script and I have tried that with no luck. I had tried that variant before posting when I was just thrashing for a solution.
I'm not familiar with the inner workings of ODBC at all but the ODBC docs do state Pooling should be under [ODBC]. I still tried it in both places with no luck.
It also states CPTimeout should live under the DSN. Is that an ODBC specific property or is it something that each drive much implement?
@sean256 We are currently working on this and should have an update for you in the next 1-2 months.
@sean256 Just an update: we are working on a fix for this. We will be completing the fix and testing it in the next 1-2 weeks. If the results are as expected we will release it in our next preview release.
@sean256 the 4.1.6 release on github or the 4.1.6.1 release on PECL enables connection pooling for unixODBC 2.3.1. The readme and changelog contains a list of limitations or issues you may encounter when you use connection pooling.
By default connection pooling is disabled. To enable, in odbcinst.ini, add Pooling=Yes to the [ODBC] section and a positive CPTimeout value to [ODBC Driver 13 for SQL Server] section. So minimally it should look like:
[ODBC]
Pooling=Yes
[ODBC Driver 13 for SQL Server]
Description=Microsoft ODBC Driver 13 for SQL Server
Driver=/opt/microsoft/msodbcsql/lib64/libmsodbcsql-13.1.so.3.0
UsageCount=1
CPTimeout=120
See http://www.unixodbc.org/doc/conn_pool.html for detailed instructions.
Enabling connection pooling with unixODBC 2.3.4 will not work with pdo_sqlsrv but we have it on our backlog to fix.
@champloo11 @sean256 have you had a chance to try this?
@meet-bhagdev
I'm having a compile error for the PECL package mentioned by @v-dareck
Here is what I have installed:
msodbcsql.x86_64 13.1.4.0-1
unixODBC.x86_64 2.3.1-11.el7
php.x86_64 7.1.1-2.el7.remi
php-cli.x86_64 7.1.1-2.el7.remi
php-common.x86_64 7.1.1-2.el7.remi
php-devel.x86_64 7.1.1-2.el7.remi
php-json.x86_64 7.1.1-2.el7.remi
php-pdo.x86_64 7.1.1-2.el7.remi
php-pear.noarch 1:1.10.1-10.el7.remi
php-process.x86_64 7.1.1-2.el7.remi
php-xml.x86_64 7.1.1-2.el7.remi
The output for sudo pecl install
downloading pdo_sqlsrv-4.1.6.1.tgz ...
Starting to download pdo_sqlsrv-4.1.6.1.tgz (181,014 bytes)
..............................done: 181,014 bytes
34 source files, building
running: phpize
Configuring for:
PHP Api Version: 20160303
Zend Module Api No: 20160303
Zend Extension Api No: 320160303
building in /var/tmp/pear-build-rootgcvbtA/pdo_sqlsrv-4.1.6.1
running: /var/tmp/pdo_sqlsrv/configure --with-php-config=/usr/bin/php-config
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for a sed that does not truncate output... /usr/bin/sed
checking for cc... cc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether cc accepts -g... yes
checking for cc option to accept ISO C89... none needed
checking how to run the C preprocessor... cc -E
checking for icc... no
checking for suncc... no
checking whether cc understands -c and -o together... yes
checking for system library directory... lib
checking if compiler supports -R... no
checking if compiler supports -Wl,-rpath,... yes
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking target system type... x86_64-unknown-linux-gnu
checking for PHP prefix... /usr
checking for PHP includes... -I/usr/include/php -I/usr/include/php/main -I/usr/include/php/TSRM -I/usr/include/php/Zend -I/usr/include/php/ext -I/usr/include/php/ext/date/lib
checking for PHP extension directory... /usr/lib64/php/modules
checking for PHP installed headers prefix... /usr/include/php
checking if debug is enabled... no
checking if zts is enabled... no
checking for re2c... no
configure: WARNING: You will need re2c 0.13.4 or later if you want to regenerate PHP parsers.
checking for gawk... gawk
checking for pdo_sqlsrv support... yes, shared
checking for PDO includes... checking for PDO includes... /usr/include/php/ext
checking for PDO_SQLSRV headers... /var/tmp/pdo_sqlsrv/shared/
checking for g++... g++
checking whether we are using the GNU C++ compiler... yes
checking whether g++ accepts -g... yes
checking how to run the C++ preprocessor... g++ -E
checking how to print strings... printf
checking for a sed that does not truncate output... (cached) /usr/bin/sed
checking for fgrep... /usr/bin/grep -F
checking for ld used by cc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking whether the shell understands some XSI constructs... yes
checking whether the shell understands "+="... yes
checking how to convert x86_64-unknown-linux-gnu file names to x86_64-unknown-linux-gnu format... func_convert_file_noop
checking how to convert x86_64-unknown-linux-gnu file names to toolchain format... func_convert_file_noop
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for dlltool... no
checking how to associate runtime and link libraries... printf %s\n
checking for ar... ar
checking for archiver @FILE support... @
checking for strip... strip
checking for ranlib... ranlib
checking for gawk... (cached) gawk
checking command to parse /usr/bin/nm -B output from cc object... ok
checking for sysroot... no
checking for mt... no
checking if : is a manifest tool... no
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking for objdir... .libs
checking if cc supports -fno-rtti -fno-exceptions... no
checking for cc option to produce PIC... -fPIC -DPIC
checking if cc PIC flag -fPIC -DPIC works... yes
checking if cc static flag -static works... no
checking if cc supports -c -o file.o... yes
checking if cc supports -c -o file.o... (cached) yes
checking whether the cc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... no
checking how to run the C++ preprocessor... g++ -E
checking for ld used by g++... /usr/bin/ld -m elf_x86_64
checking if the linker (/usr/bin/ld -m elf_x86_64) is GNU ld... yes
checking whether the g++ linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking for g++ option to produce PIC... -fPIC -DPIC
checking if g++ PIC flag -fPIC -DPIC works... yes
checking if g++ static flag -static works... no
checking if g++ supports -c -o file.o... yes
checking if g++ supports -c -o file.o... (cached) yes
checking whether the g++ linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking dynamic linker characteristics... (cached) GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
configure: creating ./config.status
config.status: creating config.h
config.status: executing libtool commands
running: make
/bin/sh /var/tmp/pear-build-rootgcvbtA/pdo_sqlsrv-4.1.6.1/libtool --mode=compile g++ -I/usr/include/php/ext -std=c++11 -I. -I/var/tmp/pdo_sqlsrv -DPHP_ATOM_INC -I/var/tmp/pear-build-rootgcvbtA/pdo_sqlsrv-4.1.6.1/include -I/var/tmp/pear-build-rootgcvbtA/pdo_sqlsrv-4.1.6.1/main -I/var/tmp/pdo_sqlsrv -I/usr/include/php -I/usr/include/php/main -I/usr/include/php/TSRM -I/usr/include/php/Zend -I/usr/include/php/ext -I/usr/include/php/ext/date/lib -I/var/tmp/pdo_sqlsrv/shared/ -DHAVE_CONFIG_H -std=c++11 -c /var/tmp/pdo_sqlsrv/pdo_dbh.cpp -o pdo_dbh.lo
libtool: compile: g++ -I/usr/include/php/ext -std=c++11 -I. -I/var/tmp/pdo_sqlsrv -DPHP_ATOM_INC -I/var/tmp/pear-build-rootgcvbtA/pdo_sqlsrv-4.1.6.1/include -I/var/tmp/pear-build-rootgcvbtA/pdo_sqlsrv-4.1.6.1/main -I/var/tmp/pdo_sqlsrv -I/usr/include/php -I/usr/include/php/main -I/usr/include/php/TSRM -I/usr/include/php/Zend -I/usr/include/php/ext -I/usr/include/php/ext/date/lib -I/var/tmp/pdo_sqlsrv/shared/ -DHAVE_CONFIG_H -std=c++11 -c /var/tmp/pdo_sqlsrv/pdo_dbh.cpp -fPIC -DPIC -o .libs/pdo_dbh.o
In file included from /var/tmp/pdo_sqlsrv/shared/typedefs_for_linux.h:23:0,
from /var/tmp/pdo_sqlsrv/shared/xplat_winnls.h:24,
from /var/tmp/pdo_sqlsrv/shared/FormattedPrint.h:24,
from /var/tmp/pdo_sqlsrv/shared/core_sqlsrv.h:41,
from /var/tmp/pdo_sqlsrv/php_pdo_sqlsrv.h:23,
from /var/tmp/pdo_sqlsrv/pdo_dbh.cpp:20:
/var/tmp/pdo_sqlsrv/shared/xplat.h:30:17: fatal error: sql.h: No such file or directory
#include <sql.h>
^
compilation terminated.
make: *** [pdo_dbh.lo] Error 1
ERROR: `make' failed
@sean256 can you install the unixODBC-devel package?
That should fix the missing sql.h error.
@v-dareck
I got it building with PECL now.
I can not get connection pooling to work. After each run the connection is still disconnected.
Here is my odbcinst.ini
[ODBC]
Pooling=Yes
Trace = yes
TraceFile =/tmp/odbc_trace.log
[ODBC Driver 13 for SQL Server]
Description=Microsoft ODBC Driver 13 for SQL Server
Driver=/opt/microsoft/msodbcsql/lib64/libmsodbcsql-13.1.so.4.0
UsageCount=1
CPTimeout=120
Here is the end of the trace output, please note the same SQLDisconnect. This happens immediately after the php script is executed.
[ODBC][4650][1487116813.657018][SQLEndTran.c][574]
Exit:[SQL_SUCCESS]
[ODBC][4650][1487116813.657029][SQLDisconnect.c][208]
Entry:
Connection = 0x7fbcc129d630
[ODBC][4650][1487116813.657041][SQLDisconnect.c][308]
Exit:[SQL_SUCCESS]
[ODBC][4650][1487116813.657052][SQLFreeHandle.c][284]
Entry:
Handle Type = 2
Input Handle = 0x7fbcc129d630
[ODBC][4650][1487116813.657074][SQLFreeHandle.c][333]
Exit:[SQL_SUCCESS]
[ODBC][4650][1487116813.657148][SQLFreeHandle.c][219]
Entry:
Handle Type = 1
Input Handle = 0x7fbcc129ba80
I also verified on the SQL server by using exec sp_who that the connection is killed off.
@sean256 the unixODBC log file will always show SQLDisconnect call. When pooling is enabled unixODBC will put the connection back into a pool during the SQLDisconnect call.
Did you check the events in SQL Profiler?
When pooling is on there should be less login/logout. Also if your PHP process exists all the connections are closed.
@v-dareck That is not how it works on windows using the sqlsrv drivers.
On Windows when using php and apache/iis the connections stay open for the specified amount of time after the request is done. The next request will pull from the pool instead of opening a new one.
I just verified this on our Windows server.
I did find that when executing the script solo via command line on windows the connection does close after excecution. This tells me the connection is tied to Apache when used and not the php process. With that said I cannot get the connections to stay alive on linux even behind Apache.
I am still toying around with it and will provide updates if I discover anything.

This screenshot is the sleeping connection after hitting a test script on Windows via Apache.
@v-dareck good news!
After rebooting the VM pooling does work when behind apache. It's behavior is a little different but it's working!
The pool size seems to be fixed at 6 and the connections don't actually close unless the a connection is requested and the only thing left are connections that are over the CPTimeout.
Do you know how to change the pool size?
@sean256
Some more details about how connection pooling works in unixODBC are detailed in the links below. While they do not refer to the php driver the behavior is the same. Timed out connections are only closed when another connection is made.
http://www.easysoft.com/products/data_access/odbc_odbc_bridge/performance_white_paper.html#2_3
http://www.easysoft.com/products/data_access/odbc_oracle_driver/performance_tuning_guide.html#connection_pooling
I didn't notice anything about connection pool size in unixODBC docs or browsing through their code. There is a connection limit to SQL Server (https://msdn.microsoft.com/en-us/library/ms187030.aspx). So unless you set that limit to 6 I'm not sure why it is fixed. Could it be an Apache setting?
@v-dareck Enabling connection pooling with unixODBC 2.3.4 will not work with pdo_sqlsrv but we have it on our backlog to fix.
Have any updation regarding this ?
You're highly recommended to install unixODBC 2.3.7. Also, upgrade to ODBC Driver 17 if you have ODBC Driver 13 only. This will install unixODBC 2.3.7 as a dependency.
If you still have problems, please create a new issue.
Most helpful comment
@sean256 Just an update: we are working on a fix for this. We will be completing the fix and testing it in the next 1-2 weeks. If the results are as expected we will release it in our next preview release.