2015-03-04 10:47:53 +00:00
|
|
|
|
#include <DB/IO/WriteBufferAIO.h>
|
|
|
|
|
#include <DB/Common/ProfileEvents.h>
|
|
|
|
|
#include <DB/Core/ErrorCodes.h>
|
|
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
WriteBufferAIO::WriteBufferAIO(const std::string & filename_, size_t buffer_size_, int flags_, mode_t mode_,
|
|
|
|
|
char * existing_memory_)
|
|
|
|
|
: BufferWithOwnMemory(buffer_size_, existing_memory_, BLOCK_SIZE),
|
|
|
|
|
flush_buffer(BufferWithOwnMemory(buffer_size_, nullptr, BLOCK_SIZE)),
|
2015-03-11 14:32:32 +00:00
|
|
|
|
request_ptrs{ &request }, events(1), filename(filename_)
|
2015-03-04 10:47:53 +00:00
|
|
|
|
{
|
|
|
|
|
ProfileEvents::increment(ProfileEvents::FileOpen);
|
|
|
|
|
|
2015-03-11 11:31:09 +00:00
|
|
|
|
int open_flags = (flags_ == -1) ? (O_WRONLY | O_TRUNC | O_CREAT) : flags_;
|
2015-03-04 10:47:53 +00:00
|
|
|
|
open_flags |= O_DIRECT;
|
|
|
|
|
|
2015-03-06 10:29:58 +00:00
|
|
|
|
fd = ::open(filename.c_str(), open_flags, mode_);
|
2015-03-04 10:47:53 +00:00
|
|
|
|
if (fd == -1)
|
2015-03-05 11:57:54 +00:00
|
|
|
|
{
|
|
|
|
|
got_exception = true;
|
2015-03-11 11:31:09 +00:00
|
|
|
|
auto error_code = (errno == ENOENT) ? ErrorCodes::FILE_DOESNT_EXIST : ErrorCodes::CANNOT_OPEN_FILE;
|
|
|
|
|
throwFromErrno("Cannot open file " + filename, error_code);
|
2015-03-05 11:57:54 +00:00
|
|
|
|
}
|
2015-03-11 13:40:15 +00:00
|
|
|
|
|
|
|
|
|
::memset(&request, 0, sizeof(request));
|
2015-03-04 10:47:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteBufferAIO::~WriteBufferAIO()
|
|
|
|
|
{
|
2015-03-05 11:57:54 +00:00
|
|
|
|
if (!got_exception)
|
2015-03-04 10:47:53 +00:00
|
|
|
|
{
|
2015-03-05 11:57:54 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
next();
|
|
|
|
|
waitForCompletion();
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
tryLogCurrentException(__PRETTY_FUNCTION__);
|
|
|
|
|
}
|
2015-03-04 10:47:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 11:57:54 +00:00
|
|
|
|
if (fd != -1)
|
|
|
|
|
::close(fd);
|
2015-03-04 10:47:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 16:34:00 +00:00
|
|
|
|
off_t WriteBufferAIO::seek(off_t off, int whence)
|
2015-03-05 14:21:55 +00:00
|
|
|
|
{
|
2015-03-10 16:34:00 +00:00
|
|
|
|
if ((off % DB::WriteBufferAIO::BLOCK_SIZE) != 0)
|
2015-03-10 17:14:07 +00:00
|
|
|
|
throw Exception("Invalid offset for WriteBufferAIO::seek", ErrorCodes::AIO_UNALIGNED_SIZE_ERROR);
|
2015-03-10 16:34:00 +00:00
|
|
|
|
|
2015-03-05 14:21:55 +00:00
|
|
|
|
waitForCompletion();
|
|
|
|
|
|
2015-03-10 16:34:00 +00:00
|
|
|
|
off_t res = ::lseek(fd, off, whence);
|
2015-03-05 14:21:55 +00:00
|
|
|
|
if (res == -1)
|
|
|
|
|
{
|
|
|
|
|
got_exception = true;
|
2015-03-06 10:29:58 +00:00
|
|
|
|
throwFromErrno("Cannot seek through file " + filename, ErrorCodes::CANNOT_SEEK_THROUGH_FILE);
|
2015-03-05 14:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WriteBufferAIO::truncate(off_t length)
|
|
|
|
|
{
|
2015-03-10 16:34:00 +00:00
|
|
|
|
if ((length % DB::WriteBufferAIO::BLOCK_SIZE) != 0)
|
2015-03-10 17:14:07 +00:00
|
|
|
|
throw Exception("Invalid length for WriteBufferAIO::ftruncate", ErrorCodes::AIO_UNALIGNED_SIZE_ERROR);
|
2015-03-10 16:34:00 +00:00
|
|
|
|
|
2015-03-05 14:21:55 +00:00
|
|
|
|
waitForCompletion();
|
|
|
|
|
|
|
|
|
|
int res = ::ftruncate(fd, length);
|
|
|
|
|
if (res == -1)
|
|
|
|
|
{
|
|
|
|
|
got_exception = true;
|
2015-03-06 10:29:58 +00:00
|
|
|
|
throwFromErrno("Cannot truncate file " + filename, ErrorCodes::CANNOT_TRUNCATE_FILE);
|
2015-03-05 14:21:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 16:34:00 +00:00
|
|
|
|
void WriteBufferAIO::sync()
|
2015-03-04 10:47:53 +00:00
|
|
|
|
{
|
2015-03-10 17:14:07 +00:00
|
|
|
|
/// Если в буфере ещё остались данные - запишем их.
|
|
|
|
|
next();
|
2015-03-10 16:34:00 +00:00
|
|
|
|
waitForCompletion();
|
2015-03-10 17:14:07 +00:00
|
|
|
|
|
|
|
|
|
/// Попросим ОС сбросить данные на диск.
|
|
|
|
|
int res = ::fsync(fd);
|
|
|
|
|
if (res == -1)
|
2015-03-11 13:15:19 +00:00
|
|
|
|
{
|
|
|
|
|
got_exception = true;
|
2015-03-10 17:14:07 +00:00
|
|
|
|
throwFromErrno("Cannot fsync " + getFileName(), ErrorCodes::CANNOT_FSYNC);
|
2015-03-11 13:15:19 +00:00
|
|
|
|
}
|
2015-03-04 10:47:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WriteBufferAIO::nextImpl()
|
|
|
|
|
{
|
|
|
|
|
if (!offset())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
waitForCompletion();
|
|
|
|
|
swapBuffers();
|
|
|
|
|
|
2015-03-10 17:14:07 +00:00
|
|
|
|
/// Создать запрос.
|
2015-03-11 13:26:24 +00:00
|
|
|
|
request.aio_lio_opcode = IOCB_CMD_PWRITE;
|
|
|
|
|
request.aio_fildes = fd;
|
|
|
|
|
request.aio_buf = reinterpret_cast<UInt64>(flush_buffer.buffer().begin());
|
|
|
|
|
request.aio_nbytes = flush_buffer.offset();
|
|
|
|
|
request.aio_offset = total_bytes_written;
|
|
|
|
|
request.aio_reqprio = 0;
|
2015-03-04 10:47:53 +00:00
|
|
|
|
|
2015-03-11 13:26:24 +00:00
|
|
|
|
if ((request.aio_nbytes % BLOCK_SIZE) != 0)
|
2015-03-06 10:29:58 +00:00
|
|
|
|
{
|
|
|
|
|
got_exception = true;
|
2015-03-10 17:14:07 +00:00
|
|
|
|
throw Exception("Illegal attempt to write unaligned data to file " + filename, ErrorCodes::AIO_UNALIGNED_SIZE_ERROR);
|
2015-03-06 10:29:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 17:14:07 +00:00
|
|
|
|
/// Отправить запрос.
|
2015-03-04 10:47:53 +00:00
|
|
|
|
while (io_submit(aio_context.ctx, request_ptrs.size(), &request_ptrs[0]) < 0)
|
|
|
|
|
if (errno != EINTR)
|
2015-03-05 11:57:54 +00:00
|
|
|
|
{
|
|
|
|
|
got_exception = true;
|
2015-03-06 10:29:58 +00:00
|
|
|
|
throw Exception("Cannot submit request for asynchronous IO on file " + filename, ErrorCodes::AIO_SUBMIT_ERROR);
|
2015-03-05 11:57:54 +00:00
|
|
|
|
}
|
2015-03-04 10:47:53 +00:00
|
|
|
|
|
|
|
|
|
is_pending_write = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WriteBufferAIO::waitForCompletion()
|
|
|
|
|
{
|
|
|
|
|
if (is_pending_write)
|
|
|
|
|
{
|
|
|
|
|
while (io_getevents(aio_context.ctx, events.size(), events.size(), &events[0], nullptr) < 0)
|
|
|
|
|
if (errno != EINTR)
|
2015-03-05 11:57:54 +00:00
|
|
|
|
{
|
|
|
|
|
got_exception = true;
|
2015-03-06 10:29:58 +00:00
|
|
|
|
throw Exception("Failed to wait for asynchronous IO completion on file " + filename, ErrorCodes::AIO_COMPLETION_ERROR);
|
2015-03-05 11:57:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-11 13:15:19 +00:00
|
|
|
|
is_pending_write = false;
|
|
|
|
|
|
|
|
|
|
if (events[0].res < 0)
|
|
|
|
|
{
|
|
|
|
|
got_exception = true;
|
|
|
|
|
throw Exception("Asynchronous write error on file " + filename, ErrorCodes::AIO_WRITE_ERROR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t bytes_written = static_cast<size_t>(events[0].res);
|
2015-03-05 11:57:54 +00:00
|
|
|
|
if (bytes_written < flush_buffer.offset())
|
|
|
|
|
{
|
|
|
|
|
got_exception = true;
|
2015-03-06 10:29:58 +00:00
|
|
|
|
throw Exception("Asynchronous write error on file " + filename, ErrorCodes::AIO_WRITE_ERROR);
|
2015-03-05 11:57:54 +00:00
|
|
|
|
}
|
2015-03-04 10:47:53 +00:00
|
|
|
|
|
2015-03-11 13:15:19 +00:00
|
|
|
|
total_bytes_written += bytes_written;
|
2015-03-04 10:47:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 11:57:54 +00:00
|
|
|
|
void WriteBufferAIO::swapBuffers() noexcept
|
2015-03-04 10:47:53 +00:00
|
|
|
|
{
|
2015-03-05 08:18:16 +00:00
|
|
|
|
buffer().swap(flush_buffer.buffer());
|
|
|
|
|
std::swap(position(), flush_buffer.position());
|
2015-03-04 10:47:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|