Python 2.7 FreeTDS和PyODBC随机连接从服务器断开

Python 2.7 FreeTDS和PyODBC随机连接从服务器断开,python-2.7,pandas,sqlalchemy,pyodbc,freetds,Python 2.7,Pandas,Sqlalchemy,Pyodbc,Freetds,出于某种原因,以下堆栈跟踪在启动进程大约16小时或更长时间后首先显示,然后每隔一到两小时显示一次。我在其他服务器上每天、每小时和每隔几分钟都有其他作业,但它们没有得到堆栈跟踪。我能看到的唯一区别是,这段代码每个调度器有多个作业,而其他的没有 堆栈跟踪: Traceback (most recent call last): File "app.py", line 24, in _run_client h.run_hourly(start, end) File "/home/foo/

出于某种原因,以下堆栈跟踪在启动进程大约16小时或更长时间后首先显示,然后每隔一到两小时显示一次。我在其他服务器上每天、每小时和每隔几分钟都有其他作业,但它们没有得到堆栈跟踪。我能看到的唯一区别是,这段代码每个调度器有多个作业,而其他的没有

堆栈跟踪:

Traceback (most recent call last):
  File "app.py", line 24, in _run_client
    h.run_hourly(start, end)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/foo/client/bar/helper.py", line 153, in run_hourly
    _run(_HOURLY_REQUEST, frequency, start, end, duration)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/foo/client/bar/helper.py", line 117, in _run
    bars = dr.get_bars(frequency)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/foo/client/bar/data_requests.py", line 35, in get_bars
    df = pd.read_sql(query, _ENGINE, params=params)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/pandas/io/sql.py", line 415, in read_sql
chunksize=chunksize)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/pandas/io/sql.py", line 1084, in read_query
    result = self.execute(*args)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/pandas/io/sql.py", line 975, in execute
    return self.connectable.execute(*args, **kwargs)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 2055, in execute
    return connection.execute(statement, *multiparams, **params)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 945, in execute
    return meth(self, multiparams, params)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/sql/elements.py", line 263, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement
compiled_sql, distilled_params
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1189, in _execute_context
context)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1386, in _handle_dbapi_exception
    self._autorollback()
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 824, in _autorollback
    self._root._rollback_impl()
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 703, in _rollback_impl
    self._handle_dbapi_exception(e, None, None, None, None)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1315, in _handle_dbapi_exception
    exc_info
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 202, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 701, in _rollback_impl
    self.engine.dialect.do_rollback(self.connection)
  File "/home/foo/anaconda2/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 439, in do_rollback
    dbapi_connection.rollback()
DBAPIError: (pyodbc.Error) ('08S01', '[08S01] [FreeTDS][SQL Server]Write to the server failed (20006) (SQLEndTran)')
服务器与下面提到的版本相匹配

版本:
操作系统:Ubuntu 16.04.1
FreeTDS软件包版本:FreeTDS通用:0.91-6.1build1,tdsodbc:amd64:0.91-6.1build1 PyODBC:3.0.10
SQL炼金术:1.1.4

TSQL输出:

Compile-time settings (established with the "configure" script)
                        Version: freetds v0.91
         freetds.conf directory: /etc/freetds
 MS db-lib source compatibility: no
    Sybase binary compatibility: yes
                  Thread safety: yes
                  iconv library: yes
                    TDS version: 4.2
                          iODBC: no
                       unixodbc: yes
          SSPI "trusted" logins: no
                       Kerberos: yes
测试代码:

app.py

from datetime import datetime, timedelta
from dateutil import relativedelta
import traceback

from apscheduler.schedulers.blocking import BlockingScheduler
from bar import helper as h


def _run_client(resolution):
    try:
        if resolution == "hourly":
            # Had to create a temporary variable to make native datetimes
            t = datetime.utcnow() - timedelta(hours=1)
            end = datetime(t.year, t.month, t.day, t.hour)
            start = end - timedelta(hours=1)
            h.run_hourly(start, end)
        elif resolution == "daily":
            # Had to create a temporary variable to make native datetimes
            t = datetime.utcnow().date() - timedelta(days=1)
            end = datetime(t.year, t.month, t.day)
            start = end - timedelta(days=1)
            h.run_daily(start, end)
        else:
            # Had to create a temporary variable to make native datetimes
            t = datetime.utcnow().date().replace(
                day=1) - relativedelta.relativedelta(months=1)
            end = datetime(t.year, t.month, t.day)
            start = end - relativedelta.relativedelta(months=1)
            h.run_monthly(start, end)
    except:
        print "Current run failed:\n%s" % traceback.format_exc()


def _get_hourly_job(sched):
    args = ["hourly"]
    job = sched.add_job(_run_client, args=args, trigger="cron", hour="*", minute="0")
    return job


def _get_daily_job(sched):
    args = ["daily"]
    job = sched.add_job(_run_client, args=args, trigger="cron", hour="4", minute="0")
    return job


def _get_monthly_job(sched):
    args = ["monthly"]
    job = sched.add_job(_run_client, args=args, trigger="cron", day="1", hour="0", minute="0")
    return job

if __name__ == "__main__":
    sched = BlockingScheduler()
    hourly_job = _get_hourly_job(sched)
    daily_job = _get_daily_job(sched)
    monthly_job = _get_monthly_job(sched)

    try:
        sched.start()
    except:
        # Remove the jobs from memory since they finished
        hourly_job.remove()
        daily_job.remove()
        monthly_job.remove()

        sched.shutdown()
helper.py

from datetime import timedelta

import data_requests as dr

_HOURLY_REQUEST = Foo()
_HOURLY_REQUEST.resolution = "hourly"

_DAILY_REQUEST = Foo()
_DAILY_REQUEST.resolution = "daily"

_MONTHLY_REQUEST = Foo()
_MONTHLY_REQUEST.resolution = "monthly"


def _run(request, frequency, start, end, duration):
    bars = dr.get_bars(frequency)

    if bars.empty: 
        return None
    print "Bars = %i" % len(bars)


def run_daily(start, end):
    frequency = 86400
    duration = timedelta(hours=4)
    _run(_DAILY_REQUEST, frequency, start, end, duration)


def run_hourly(start, end):
    frequency = 3600
    duration = timedelta(minutes=30)
    _run(_HOURLY_REQUEST, frequency, start, end, duration)


def run_monthly(start, end):
    frequency = 1209600
    duration = timedelta(days=1)
    _run(_MONTHLY_REQUEST, frequency, start, end, duration)
data_requests.py

import pandas as pd
from sqlalchemy import create_engine, exc
from sqlalchemy.sql import text

_DB = "mssql+pyodbc://foo@stg-foo:FooBar@stg-foo.database.secure.windows.net:1433/foo?driver=/usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so&tds_version=7.2"
_ENGINE = create_engine(_DB)


def get_bars(frequency):
    query = text("""SELECT h.foo_id, s.id AS bar_id, u.timezone
                FROM foo h
                INNER JOIN bar s ON h.foo_id = s.foo_id
                AND s.frequency <= :frequency
                INNER JOIN test u ON u.id = h.test_id""")
    params = {
        "frequency": int(frequency)
    }

    try:
        df = pd.read_sql(query, _ENGINE, params=params)
    except exc.DBAPIError, e:
        # If connection is invalid (e.g. database restarted) execute the query
        # again
        if e.connection_invalidated:
            df = pd.read_sql(query, _ENGINE, params=params)
        else:
            raise e

    return df
将熊猫作为pd导入
从sqlalchemy导入创建引擎,exc
从sqlalchemy.sql导入文本
_DB=“mssql+pyodbc://foo@stg foo:FooBar@stg-foo.database.secure.windows.net:1433/foo?driver=/usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so&tds_version=7.2“
_引擎=创建引擎(_DB)
def get_条(频率):
query=text(“”)选择h.foo\u id,s.id作为bar\u id,u.timezone
来自福华
h.foo_id=s.foo_id上的内部连接条s

和s.frequency我找到了错误的原因。这似乎毕竟是一个编码问题。这花了很长时间才弄清楚,但这与我如何使用pandas进行调用有关。显然,pandas在传递引擎实例时似乎没有关闭连接。下面是我用来修复该问题的代码

def _get_df(query, params=None):
    try:
        with _ENGINE.begin() as conn:
            df = pd.read_sql(query, conn, params=params)
    except exc.DBAPIError, e:
        # If connection is invalid (e.g. database restarted) execute the query
        # again
        if e.connection_invalidated:
            with _ENGINE.begin() as conn:
                df = pd.read_sql(query, conn, params=params)
        else:
            raise e
    return df

该错误似乎来自比Python层更高的堆栈。考虑到时间安排,SQL Server端是否有任何类型的计划任务可能在特定时间运行,从而产生锁定问题?错误中的SQL查询似乎不需要花费一些时间。当您运行
tsql-C
wh正在报告TDS协议的at版本?@FlipperPA我不知道。运行tsql-C后,与pymssql一起安装的anaconda freetds和安装的package manager freetds之间可能存在冲突。我将再运行一个小时,看看这是否是问题所在。@FlipperPA我已经更新了h更多详细信息。这似乎不是冲突问题。我检查了主数据库中的sys.event_log表,它没有显示除成功连接以外的任何信息。对我来说,这看起来像是连接问题,但出于某种原因,我在SQL Alchemy文档中使用的代码没有处理这种情况。