Skip to content

[BUG] pg数据库通过url配置socketTimeout单位为毫秒,connectTimeout单位为秒 #6391

Open
@shinerio

Description

@shinerio

Database Type

postgresql

Database Version

any

Druid Version

1.2.23

JDK Version

OpenJDK 17

Error SQL

case1. 用户通过druid properties配置socketTimeout和connectTimout参数

  1. 低版本的druid不会做时间转换,会直接透传给驱动,那么驱动会使用properties设置超时时间,此时单位是秒
  2. 高版本的druid对时间做了毫秒到秒的转换,要求用户做不兼容改动,将properties中配置时间x1000,配置为毫秒,使用者有可能并不知情,导致期望超时行为发生变化,此为不兼容改动

case2. 用户通过jdbc url设置socketTimeout和connectTimeout参数

实际创建连接时,pg驱动中会从url中提取socketTimeout和connectTimeout参数,则druid提供的properties会被驱动覆盖,而驱动期望的单位是秒。但是DruidAbstarctDataSource中期望的timeout参数是以毫秒为单位,pg下会将毫秒转换为秒,此情况要求使用者url中配置的是毫秒单位。此外,setNetworkTimeout也是druid解析后的结果,对使用者要求是毫秒为单位。这就会导致socketTimeout需要按照你们期望的以毫秒为单位在url中配置。但是!!!connectTimeout是在驱动里面实现的,默认会乘1000,且druid后续不会像setNetowkTimeout一样有覆盖行为,这就导致用户配置url就必须以秒为单位配置。

结论:通过url配置的方式要求connectTimeout以秒为单位,socketTimeout以毫秒为单位配置,两个配置单位不一致

pg驱动里面实现的是秒,驱动会再乘1000的

 private PGStream tryConnect(Properties info, SocketFactory socketFactory, HostSpec hostSpec, SslMode sslMode, GSSEncMode gssEncMode) throws SQLException, IOException {
    int connectTimeout = PGProperty.CONNECT_TIMEOUT.getInt(info) * 1000;
    String user = PGProperty.USER.getOrDefault(info);
    String database = PGProperty.PG_DBNAME.getOrDefault(info);
    SslNegotiation sslNegotiation = SslNegotiation.of((String)Nullness.castNonNull(PGProperty.SSL_NEGOTIATION.getOrDefault(info)));
    if (user == null)
      throw new PSQLException(GT.tr("User cannot be null", new Object[0]), PSQLState.INVALID_NAME); 
    if (database == null)
      throw new PSQLException(GT.tr("Database cannot be null", new Object[0]), PSQLState.INVALID_NAME); 
    int maxSendBufferSize = PGProperty.MAX_SEND_BUFFER_SIZE.getInt(info);
    PGStream newStream = new PGStream(socketFactory, hostSpec, connectTimeout, maxSendBufferSize);
    try {
      int socketTimeout = PGProperty.SOCKET_TIMEOUT.getInt(info);
      if (socketTimeout > 0)
        newStream.setNetworkTimeout(socketTimeout * 1000); 
      String maxResultBuffer = PGProperty.MAX_RESULT_BUFFER.getOrDefault(info);
      newStream.setMaxResultBuffer(maxResultBuffer);
      boolean requireTCPKeepAlive = PGProperty.TCP_KEEP_ALIVE.getBoolean(info);
      newStream.getSocket().setKeepAlive(requireTCPKeepAlive);
      boolean requireTCPNoDelay = PGProperty.TCP_NO_DELAY.getBoolean(info);
      newStream.getSocket().setTcpNoDelay(requireTCPNoDelay);
      int receiveBufferSize = PGProperty.RECEIVE_BUFFER_SIZE.getInt(info);
      if (receiveBufferSize > -1)
        if (receiveBufferSize > 0) {
          newStream.getSocket().setReceiveBufferSize(receiveBufferSize);
        } else {
          LOGGER.log(Level.WARNING, "Ignore invalid value for receiveBufferSize: {0}", 
              Integer.valueOf(receiveBufferSize));
        }  
      int sendBufferSize = PGProperty.SEND_BUFFER_SIZE.getInt(info);
      if (sendBufferSize > -1)
        if (sendBufferSize > 0) {
          newStream.getSocket().setSendBufferSize(sendBufferSize);
        } else {
          LOGGER.log(Level.WARNING, "Ignore invalid value for sendBufferSize: {0}", Integer.valueOf(sendBufferSize));
        }  
      if (LOGGER.isLoggable(Level.FINE)) {
        LOGGER.log(Level.FINE, "Receive Buffer Size is {0}", 
            Integer.valueOf(newStream.getSocket().getReceiveBufferSize()));
        LOGGER.log(Level.FINE, "Send Buffer Size is {0}", 
            Integer.valueOf(newStream.getSocket().getSendBufferSize()));
      } 
      if (sslNegotiation != SslNegotiation.DIRECT)
        newStream = enableGSSEncrypted(newStream, gssEncMode, hostSpec.getHost(), info, connectTimeout); 
      if (!newStream.isGssEncrypted())
        newStream = enableSSL(newStream, sslMode, info, connectTimeout); 
      if (socketTimeout > 0)
        newStream.setNetworkTimeout(socketTimeout * 1000); 
      List<StartupParam> paramList = getParametersForStartup(user, database, info);
      sendStartupPacket(newStream, paramList);
      doAuthentication(newStream, hostSpec.getHost(), user, info);
      return newStream;
    } catch (Exception e) {
      closeStream(newStream);
      throw e;
    } 
  }

Testcase Code

No response

Stacktrace Info

No response

Error Info

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions