2009-05-14 36 views
11

Tôi có một kịch bản dài chạy mà dường như thỉnh thoảng báo lỗi sau THÔNG BÁO cấp: pg_send_query(): Không thể thiết lập kết nối sang chế độpg_send_query(): Không thể đặt kết nối với chế độ chặn?

Có vẻ như để tiếp tục gửi các truy vấn sau đó ngăn chặn, nhưng nó không rõ ràng nếu nó gửi thành công truy vấn tạo lỗi.

Đây là triệu chứng của điều gì?

Edit: Không có mục trong postgres đăng nhập đồng thời xảy ra lỗi, gợi ý này chỉ là một lỗi kết nối, không phải cái gì xảy ra sai về phía postgres' (ví dụ lẽ không phải là kết quả của postgres đâm và khởi động lại hoặc một cái gì đó)

Chỉnh sửa: Theo như tôi có thể nói, các câu lệnh INSERT của tôi thành công, theo cách này hay cách khác, khi lỗi này được kích hoạt.

Edit: trông như thế này có thể đã được cố định trong tháng Sáu năm 2013: https://bugs.php.net/bug.php?id=65015

Trả lời

23

Đó là một triệu chứng của pg_send_query() không thể chuyển thành công kết nối trở lại chế độ chặn. Nhìn vào mã nguồn trong PHP pgsql.c, bạn có thể tìm thấy:

/* {{{ proto bool pg_send_query(resource connection, string query) 
    Send asynchronous query */ 
PHP_FUNCTION(pg_send_query) 
{ 

<... snipped function setup stuff ...> 

if (PQ_SETNONBLOCKING(pgsql, 1)) { 
    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode"); 
    RETURN_FALSE; 
} 

<... snipped main function execution stuff ...> 

if (PQ_SETNONBLOCKING(pgsql, 0)) { 
    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode"); 
} 
RETURN_TRUE; 
} 

Vì vậy, lỗi được nêu ra ở cuối hàm, sau khi hoàn thành công việc chính. Điều này phù hợp với quan sát của bạn rằng các câu lệnh INSERT của bạn được thực hiện.

Toàn bộ mục đích của hai cuộc gọi PQ_SETNONBLOCKING là đặt kết nối ở chế độ không chặn để cho phép thực thi không đồng bộ và đưa nó trở lại hành vi chặn mặc định sau đó. Từ documentation of PQsetnonblocking: (PQ_SETNONBLOCKING chỉ là một bí danh được xác định cho chức năng đó):

Sets the nonblocking status of the connection.

int PQsetnonblocking(PGconn *conn, int arg); 

Sets the state of the connection to nonblocking if arg is 1, or blocking if arg is 0. Returns 0 if OK, -1 if error.

In the nonblocking state, calls to PQsendQuery, PQputline, PQputnbytes, and PQendcopy will not block but instead return an error if they need to be called again.

Note that PQexec does not honor nonblocking mode; if it is called, it will act in blocking fashion anyway.

Nhìn xa hơn vào nguồn gốc của PQsetnonblocking (trong PostgeSQLs fe-exec.c), có hai lý do khiến các cuộc gọi có thể thất bại :

/* PQsetnonblocking: 
* sets the PGconn's database connection non-blocking if the arg is TRUE 
* or makes it non-blocking if the arg is FALSE, this will not protect 
* you from PQexec(), you'll only be safe when using the non-blocking API. 
* Needs to be called only on a connected database connection. 
*/ 
int 
PQsetnonblocking(PGconn *conn, int arg) 
{ 
bool barg; 

if (!conn || conn->status == CONNECTION_BAD) 
    return -1; 

barg = (arg ? TRUE : FALSE); 

/* early out if the socket is already in the state requested */ 
if (barg == conn->nonblocking) 
    return 0; 

/* 
    * to guarantee constancy for flushing/query/result-polling behavior we 
    * need to flush the send queue at this point in order to guarantee proper 
    * behavior. this is ok because either they are making a transition _from_ 
    * or _to_ blocking mode, either way we can block them. 
    */ 
/* if we are going from blocking to non-blocking flush here */ 
if (pqFlush(conn)) 
    return -1; 

conn->nonblocking = barg; 

return 0; 
} 

Vì vậy, kết nối bị mất bằng cách nào đó hoặc pqFlush không kết thúc thành công, cho biết công cụ còn lại trong bộ đệm đầu ra kết nối.

Trường hợp đầu tiên sẽ vô hại, vì tập lệnh của bạn chắc chắn sẽ thông báo kết nối bị mất cho các cuộc gọi sau đó và phản ứng lại (hoặc không thể nhận thấy rõ hơn).

Điều này để lại trường hợp thứ hai, điều đó có nghĩa là bạn có kết nối ở trạng thái không mặc định, không chặn. Tôi không biết nếu điều này có thể ảnh hưởng đến các cuộc gọi sau đó sẽ tái sử dụng kết nối này. Nếu bạn muốn chơi nó an toàn, bạn sẽ đóng kết nối trong trường hợp này và sử dụng một kết nối mới/khác.

+5

+1 để đi thẳng đến nguồn –

+4

+1 để xác định nguyên nhân thực sự và đưa ra các đề xuất thực tế về việc cần làm. Làm tốt! –

+1

Tôi nghĩ rằng tôi đã không may thành công trong việc lặp lại lỗi này với pg_query() kết hợp với pg_pconnect(). Sử dụng pg_send_query() là không cần thiết. Các pg_query dường như ngẫu nhiên tạo ra lỗi này nếu kết nối trước đó được tự động cuộn lại khi kết nối "mới" được mua với pg_pconnect(). Sự cố được hiển thị với PHP 5.3.2 và Postgres 8.4 chạy trên Ubuntu 10.04 LTS x86-64. –

3

Có vẻ như bạn đang cố gắng sử dụng pg_send_query() chức năng cho việc gửi các truy vấn không đồng bộ để PostgreSQL. Mục đích của chức năng này là cho phép tập lệnh PHP của bạn tiếp tục thực thi mã khác trong khi đợi PostgreSQL thực thi truy vấn của bạn và sẵn sàng cho kết quả.

Ví dụ được đưa ra trong docs cho pg_send_query() gợi ý rằng bạn không nên gửi một truy vấn nếu PostgreSQL đã được nhai trên truy vấn khác:

if (!pg_connection_busy($dbconn)) { 
    pg_send_query($dbconn, "select * from authors; select count(*) from authors;"); 
} 

Có một lý do bạn đang sử dụng pg_send_query() thay vì pg_query()? Nếu bạn có thể cho phép tập lệnh của bạn chặn truy vấn thực thi truy vấn, tôi đoán (phải thừa nhận rằng không thử nó) mà bạn sẽ không thấy các lỗi này.

+1

+1 - Tôi cho rằng anh ấy đang làm điều đó một cách không đồng bộ với mục đích, cho nhận xét 'kịch bản dài', nhưng đây sẽ là giải pháp hiển nhiên nếu không phải như vậy :) –

+1

Một nhu cầu sử dụng 'pg_send_query () 'để có thể truy vấn chính xác các lỗi trong trường hợp truy vấn thất bại. Nếu không, bạn cần phải sử dụng 'pg_last_error()' mà không thực sự làm việc tốt. –

+1

Hãy coi chừng lỗi https://bugs.php.net/bug.php?id=65015 nếu bạn sử dụng 'pg_send_query()' và nói chuyện với máy chủ từ xa.Nếu chuỗi truy vấn quá dài (tính bằng byte), 'pg_send_query()' sẽ thất bại vì lỗi triển khai (thiếu xóa trước khi chạm vào danh mục). Để giải quyết sự cố, hãy cài đặt PgBouncer và sử dụng pg_connect() để kết nối với localhost PgBouncer thay vì kết nối trực tiếp với máy chủ từ xa. –

2

Điều này có thể xảy ra nếu bạn đang sử dụng chủ đề và kết nối đang được sử dụng lại. Nếu đây là trường hợp bạn có thể sử dụng PGSQL_CONNECT_FORCE_NEW như thế này:

pg_connect("...", PGSQL_CONNECT_FORCE_NEW) 

Điều này sẽ buộc một nguồn tài nguyên kết nối cơ sở dữ liệu mới, nhưng hãy thận trọng: bạn có thể chạy ra khỏi các kết nối khách hàng, vì vậy hãy cẩn thận sử dụng này đề bên trong vì vậy đừng quên sử dụng pg_close().

3

Gần đây tôi đã gặp vấn đề tương tự, và với sự trợ giúp từ Henrik Opels, câu trả lời nhận ra rằng PHP không thực sự chờ bộ đệm xả trước khi thiết lập kết nối trở lại chế độ chặn.

'Không thể đặt kết nối thành chế độ chặn' có thể lặp lại không đáng kể với các truy vấn đủ lớn để điền đệm gửi (đệm với khoảng trống ở cuối là đủ). Với các truy vấn nhỏ hơn tôi tưởng tượng nó phụ thuộc vào tải, và thay vì liên tục.

nếu bạn thực sự cần chế độ không đồng bộ sau đó thử các bản vá tại https://bugs.php.net/bug.php?id=65015

+0

Một cũng có thể cài đặt PgBouncer trên localhost và sử dụng chế độ không đồng bộ qua nó. Lỗi PHP chỉ được kích hoạt nếu máy chủ ở xa. –

+0

Tôi cũng gặp sự cố này trên các kết nối máy chủ cục bộ. Bạn có nghĩa là vấn đề sẽ biến mất nếu chúng ta sử dụng các kết nối socket? –

1

tôi gặp phải thông báo lỗi tương tự với PHP 5.6.9

Nó xảy ra khi kết nối liên tục được thực hiện bởi pg_pconnect() bị mất và pgsql.auto_reset_persistent được đặt thành Tắt.

kết nối có thể bị mất khi:

  1. PHP phiên hết hạn
  2. Kết nối đến DB timeouts
  3. Webserver/DB máy chủ được khởi động lại

Bạn có thể kiểm tra php.ini cho pgsql .auto_reset_persistent và đặt thành Trên.

Với pgsql.auto_reset_persistent kích hoạt, mỗi lần pg_pconnect() đang được gọi là, liên kết kết nối sẽ được kiểm tra, nếu nó vẫn còn hợp lệ. Điều này đòi hỏi một ít chi phí, nhưng fixies thông báo lỗi khi conncetion bị mất.

2

Tôi cũng nhận được lỗi đó. Tôi giải quyết vấn đề của mình bằng cách khởi động lại máy chủ web (Apache).

Các vấn đề liên quan