Commits

Aristarkh Zagorodnikov committed 6639e10

Added auto-retry (needed due to async oplog application in MongoDB 2.2+) and "not found" handling for missing chunks

Comments (0)

Files changed (1)

gridfs/mod_gridfs.cpp

 		apr_off_t content_length = 0;
 		std::string content_type;
 		const apr_time_t retry_threshold = request->request_time + apr_time_from_sec(config->connect_timeout);
+		bool custom_read_pref_mode = config->read_pref_mode != 0 && *config->read_pref_mode != "primary";
 		while (true)
 		{
 			try
 
 				const std::string& collection_prefix = config->collection_prefix != 0 ? *config->collection_prefix : DEFAULT_COLLECTION_PREFIX;
 				const mongo::GridFS gridfs(client, database, collection_prefix, false);
-				const bool read_pref_mode_set = config->read_pref_mode != 0;
-				mongo::GridFile gridfile = read_pref_mode_set ?
-					gridfs.findFile(filename, *config->read_pref_mode) :
-					gridfs.findFile(filename);
-				if (!gridfile.exists())
+				while (true)
 				{
-					if (read_pref_mode_set && *config->read_pref_mode != "primary")
+					try
 					{
-						gridfile = gridfs.findFile(filename);
+						mongo::GridFile gridfile = custom_read_pref_mode ?
+							gridfs.findFile(filename, *config->read_pref_mode) :
+							gridfs.findFile(filename);
 						if (!gridfile.exists())
 						{
+							if (custom_read_pref_mode)
+							{
+								custom_read_pref_mode = false;
+								gridfile = gridfs.findFile(filename);
+								if (!gridfile.exists())
+								{
+									connection->done();
+									return HTTP_NOT_FOUND;
+								}
+							}
+							else
+							{
+								connection->done();
+								return HTTP_NOT_FOUND;
+							}
+						}
+						const mongo::Date_t upload_date = gridfile.getUploadDate();
+						request->mtime = apr_time_from_sec(upload_date.toTimeT());
+						ap_set_last_modified(request);
+						const std::string& md5 = gridfile.getMD5();
+						if (!md5.empty())
+							apr_table_setn(request->headers_out, "ETag", md5.c_str());
+						if (ap_meets_conditions(request) == HTTP_NOT_MODIFIED)
+						{
+							connection->done();
+							if (!md5.empty())
+								apr_table_unset(request->headers_out, "ETag");
+							return HTTP_NOT_MODIFIED;
+						}
+						const mongo::gridfs_offset file_length = gridfile.getContentLength();
+						content_length = 0;
+						content_type = gridfile.getContentType();
+						if (file_length != 0 && request->header_only == 0)
+						{
+							const int num_chunks = gridfile.getNumChunks();
+							if (num_chunks == 0)
+							{
+								connection->done();
+								ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: No chunks available for file '%s' (database: '%s').", filename, database.c_str()); 
+								return HTTP_INTERNAL_SERVER_ERROR;
+							}
+							if (brigade == 0)
+							{
+								brigade = apr_brigade_create(request->pool, request->connection->bucket_alloc);
+								if (brigade == 0)
+								{
+									connection->done();
+									ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, request, "mod_gridfs: Failed to create brigade."); 
+									return HTTP_INTERNAL_SERVER_ERROR;
+								}
+							}
+							for (int chunk_index = 0;chunk_index < num_chunks;chunk_index++) 
+							{
+								const mongo::GridFSChunk& chunk = gridfile.getChunk(chunk_index);
+								int chunk_length;
+								const char *chunk_data = chunk.data(chunk_length);
+								if (chunk_length == 0)
+									continue;
+								const int result = apr_brigade_write(brigade, 0, 0, chunk_data, chunk_length);
+								if (result != APR_SUCCESS)
+								{
+									connection->done();
+									ap_log_rerror(APLOG_MARK, APLOG_ERR, result, request, "mod_gridfs: Failed to write chunk %d for file '%s' to brigade (length: %d).", chunk_index, filename, chunk_length); 
+									return HTTP_INTERNAL_SERVER_ERROR;
+								}
+								content_length += chunk_length;
+							}
+							if (content_length != file_length)
+							{
+								connection->done();
+								ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: Mismatching files/chunks length for file '%s' (difference: %d, database: '%s').", filename, static_cast<int>(file_length - content_length), database.c_str());
+								return HTTP_INTERNAL_SERVER_ERROR;
+							}
+						}
+						break;
+					}
+					catch (const mongo::DBException& exception)
+					{
+						const int code = exception.getCode();
+						switch (code)
+						{
+						case 10014:	//	chunk is empty!
+							if (custom_read_pref_mode)
+							{
+								ap_log_rerror(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, 0, request, "mod_gridfs: Retrying after missing chunk for file '%s' (database: '%s').", filename, database.c_str());
+								custom_read_pref_mode = false;
+								break;
+							}
+							ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: Missing chunk for file '%s' (database: '%s').", filename, database.c_str());
 							connection->done();
 							return HTTP_NOT_FOUND;
+	        	
+						default:
+							throw;
 						}
 					}
-					else
-					{
-						connection->done();
-						return HTTP_NOT_FOUND;
-					}
-				}
-				const mongo::Date_t upload_date = gridfile.getUploadDate();
-				request->mtime = apr_time_from_sec(upload_date.toTimeT());
-				ap_set_last_modified(request);
-				const std::string& md5 = gridfile.getMD5();
-				if (!md5.empty())
-					apr_table_setn(request->headers_out, "ETag", md5.c_str());
-				if (ap_meets_conditions(request) == HTTP_NOT_MODIFIED)
-				{
-					connection->done();
-					if (!md5.empty())
-						apr_table_unset(request->headers_out, "ETag");
-					return HTTP_NOT_MODIFIED;
-				}
-				const mongo::gridfs_offset file_length = gridfile.getContentLength();
-				content_length = 0;
-				content_type = gridfile.getContentType();
-				if (file_length != 0 && request->header_only == 0)
-				{
-					const int num_chunks = gridfile.getNumChunks();
-					if (num_chunks == 0)
-					{
-						connection->done();
-						ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: No chunks available for file '%s' (database: '%s').", filename, database.c_str()); 
-						return HTTP_INTERNAL_SERVER_ERROR;
-					}
-					if (brigade == 0)
-					{
-						brigade = apr_brigade_create(request->pool, request->connection->bucket_alloc);
-						if (brigade == 0)
-						{
-							connection->done();
-							ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, request, "mod_gridfs: Failed to create brigade."); 
-							return HTTP_INTERNAL_SERVER_ERROR;
-						}
-					}
-					for (int chunk_index = 0;chunk_index < num_chunks;chunk_index++) 
-					{
-						const mongo::GridFSChunk& chunk = gridfile.getChunk(chunk_index);
-						int chunk_length;
-						const char *chunk_data = chunk.data(chunk_length);
-						if (chunk_length == 0)
-							continue;
-						const int result = apr_brigade_write(brigade, 0, 0, chunk_data, chunk_length);
-						if (result != APR_SUCCESS)
-						{
-							connection->done();
-							ap_log_rerror(APLOG_MARK, APLOG_ERR, result, request, "mod_gridfs: Failed to write chunk %d for file '%s' to brigade (length: %d).", chunk_index, filename, chunk_length); 
-							return HTTP_INTERNAL_SERVER_ERROR;
-						}
-						content_length += chunk_length;
-					}
-					if (content_length != file_length)
-					{
-						connection->done();
-						ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, request, "mod_gridfs: Mismatching files/chunks length for file '%s' (difference: %d, database: '%s').", filename, static_cast<int>(file_length - content_length), database.c_str());
-						return HTTP_INTERNAL_SERVER_ERROR;
-					}
 				}
 				connection->done();
 				break;