StorageBuffer.cpp 29.8 KB
Newer Older
1
#include <boost/range/algorithm_ext/erase.hpp>
2 3 4
#include <Interpreters/InterpreterSelectQuery.h>
#include <Interpreters/InterpreterInsertQuery.h>
#include <Interpreters/InterpreterAlterQuery.h>
5
#include <Interpreters/castColumn.h>
6
#include <Interpreters/evaluateConstantExpression.h>
7
#include <Processors/Transforms/AddingMissedTransform.h>
8
#include <DataStreams/IBlockInputStream.h>
9
#include <Storages/StorageBuffer.h>
10
#include <Storages/StorageFactory.h>
11
#include <Storages/AlterCommands.h>
12 13
#include <Parsers/ASTInsertQuery.h>
#include <Parsers/ASTIdentifier.h>
14
#include <Parsers/ASTLiteral.h>
15 16
#include <Parsers/ASTExpressionList.h>
#include <Common/CurrentMetrics.h>
17
#include <Common/MemoryTracker.h>
18
#include <Common/FieldVisitors.h>
19
#include <Common/quoteString.h>
20
#include <Common/typeid_cast.h>
21
#include <Common/ProfileEvents.h>
A
alexey-milovidov 已提交
22
#include <common/logger_useful.h>
A
Alexey Milovidov 已提交
23
#include <common/getThreadId.h>
24
#include <ext/range.h>
25 26 27
#include <Processors/Transforms/ConvertingTransform.h>
#include <Processors/Transforms/FilterTransform.h>
#include <Processors/Transforms/ExpressionTransform.h>
N
Nikolai Kochetov 已提交
28
#include <Processors/Sources/SourceFromInputStream.h>
29

30

31 32
namespace ProfileEvents
{
33 34 35 36 37 38
    extern const Event StorageBufferFlush;
    extern const Event StorageBufferErrorOnFlush;
    extern const Event StorageBufferPassedAllMinThresholds;
    extern const Event StorageBufferPassedTimeMaxThreshold;
    extern const Event StorageBufferPassedRowsMaxThreshold;
    extern const Event StorageBufferPassedBytesMaxThreshold;
39 40 41 42
}

namespace CurrentMetrics
{
43 44
    extern const Metric StorageBufferRows;
    extern const Metric StorageBufferBytes;
45 46 47
}


48 49 50
namespace DB
{

51 52
namespace ErrorCodes
{
A
Alexey Milovidov 已提交
53 54
    extern const int NOT_IMPLEMENTED;
    extern const int LOGICAL_ERROR;
55
    extern const int INFINITE_LOOP;
56
    extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
57 58
}

59

60 61 62 63
StorageBuffer::StorageBuffer(
    const StorageID & table_id_,
    const ColumnsDescription & columns_,
    const ConstraintsDescription & constraints_,
64
    Context & context_,
65 66 67
    size_t num_shards_,
    const Thresholds & min_thresholds_,
    const Thresholds & max_thresholds_,
68
    const StorageID & destination_id_,
69 70 71 72 73 74
    bool allow_materialized_)
    : IStorage(table_id_)
    , global_context(context_)
    , num_shards(num_shards_), buffers(num_shards_)
    , min_thresholds(min_thresholds_)
    , max_thresholds(max_thresholds_)
75
    , destination_id(destination_id_)
76 77
    , allow_materialized(allow_materialized_)
    , log(&Logger::get("StorageBuffer (" + table_id_.getFullTableName() + ")"))
78
    , bg_pool(global_context.getBufferFlushSchedulePool())
79
{
A
Alexey Milovidov 已提交
80 81
    setColumns(columns_);
    setConstraints(constraints_);
82 83
}

P
proller 已提交
84 85
StorageBuffer::~StorageBuffer()
{
86
    flush_handle->deactivate();
P
proller 已提交
87 88
}

89

F
f1yegor 已提交
90
/// Reads from one buffer (from one block) under its mutex.
91
class BufferSource : public SourceWithProgress
92 93
{
public:
94
    BufferSource(const Names & column_names_, StorageBuffer::Buffer & buffer_, const StorageBuffer & storage)
N
Nikolai Kochetov 已提交
95
        : SourceWithProgress(storage.getSampleBlockForColumns(column_names_))
96
        , column_names(column_names_.begin(), column_names_.end()), buffer(buffer_) {}
97

98
    String getName() const override { return "Buffer"; }
99 100

protected:
101
    Chunk generate() override
102
    {
103
        Chunk res;
104

105 106 107
        if (has_been_read)
            return res;
        has_been_read = true;
108

A
Alexey Milovidov 已提交
109
        std::lock_guard lock(buffer.mutex);
110

111 112
        if (!buffer.data.rows())
            return res;
113

114 115 116
        Columns columns;
        columns.reserve(column_names.size());

117
        for (const auto & name : column_names)
118 119 120 121
            columns.push_back(buffer.data.getByName(name).column);

        UInt64 size = columns.at(0)->size();
        res.setColumns(std::move(columns), size);
122

123 124
        return res;
    }
125 126

private:
127
    Names column_names;
128 129
    StorageBuffer::Buffer & buffer;
    bool has_been_read = false;
130 131 132
};


A
alexey-milovidov 已提交
133
QueryProcessingStage::Enum StorageBuffer::getQueryProcessingStage(const Context & context, QueryProcessingStage::Enum to_stage, const ASTPtr & query_ptr) const
134
{
135
    if (destination_id)
136
    {
137
        auto destination = DatabaseCatalog::instance().getTable(destination_id);
138 139 140 141

        if (destination.get() == this)
            throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP);

142
        return destination->getQueryProcessingStage(context, to_stage, query_ptr);
143 144 145 146 147
    }

    return QueryProcessingStage::FetchColumns;
}

N
Nikolai Kochetov 已提交
148

149
Pipes StorageBuffer::read(
N
Nikolai Kochetov 已提交
150 151 152 153 154 155 156
    const Names & column_names,
    const SelectQueryInfo & query_info,
    const Context & context,
    QueryProcessingStage::Enum processed_stage,
    size_t max_block_size,
    unsigned num_streams)
{
157
    Pipes pipes_from_dst;
158

159
    if (destination_id)
160
    {
161
        auto destination = DatabaseCatalog::instance().getTable(destination_id);
162

163 164
        if (destination.get() == this)
            throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP);
165

166 167
        auto destination_lock = destination->lockStructureForShare(
                false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout);
168

169
        const bool dst_has_same_structure = std::all_of(column_names.begin(), column_names.end(), [this, destination](const String& column_name)
170
        {
171 172 173 174
            const auto & dest_columns = destination->getColumns();
            const auto & our_columns = getColumns();
            return dest_columns.hasPhysical(column_name) &&
                   dest_columns.get(column_name).type->equals(*our_columns.get(column_name).type);
175 176 177 178
        });

        if (dst_has_same_structure)
        {
179
            if (query_info.order_by_optimizer)
180
                query_info.input_sorting_info = query_info.order_by_optimizer->getInputOrder(destination);
181

182
            /// The destination table has the same structure of the requested columns and we can simply read blocks from there.
183
            pipes_from_dst = destination->read(column_names, query_info, context, processed_stage, max_block_size, num_streams);
184 185
        }
        else
186
        {
187 188 189 190
            /// There is a struct mismatch and we need to convert read blocks from the destination table.
            const Block header = getSampleBlock();
            Names columns_intersection = column_names;
            Block header_after_adding_defaults = header;
191 192
            const auto & dest_columns = destination->getColumns();
            const auto & our_columns = getColumns();
193
            for (const String & column_name : column_names)
194
            {
195
                if (!dest_columns.hasPhysical(column_name))
196
                {
197
                    LOG_WARNING(log, "Destination table " << destination_id.getNameForLogs()
198 199 200 201
                        << " doesn't have column " << backQuoteIfNeed(column_name) << ". The default values are used.");
                    boost::range::remove_erase(columns_intersection, column_name);
                    continue;
                }
A
alesapin 已提交
202 203
                const auto & dst_col = dest_columns.getPhysical(column_name);
                const auto & col = our_columns.getPhysical(column_name);
204
                if (!dst_col.type->equals(*col.type))
205
                {
206
                    LOG_WARNING(log, "Destination table " << destination_id.getNameForLogs()
207
                        << " has different type of column " << backQuoteIfNeed(column_name) << " ("
208 209
                        << dst_col.type->getName() << " != " << col.type->getName() << "). Data from destination table are converted.");
                    header_after_adding_defaults.getByName(column_name) = ColumnWithTypeAndName(dst_col.type, column_name);
210 211
                }
            }
212 213

            if (columns_intersection.empty())
214
            {
215
                LOG_WARNING(log, "Destination table " << destination_id.getNameForLogs()
216
                    << " has no common columns with block in buffer. Block of data is skipped.");
217
            }
218
            else
219
            {
220
                pipes_from_dst = destination->read(columns_intersection, query_info, context, processed_stage, max_block_size, num_streams);
221
                for (auto & pipe : pipes_from_dst)
222
                {
223 224 225 226
                    pipe.addSimpleTransform(std::make_shared<AddingMissedTransform>(
                            pipe.getHeader(), header_after_adding_defaults, getColumns().getDefaults(), context));

                    pipe.addSimpleTransform(std::make_shared<ConvertingTransform>(
A
Alexey Milovidov 已提交
227
                            pipe.getHeader(), header, ConvertingTransform::MatchColumnsMode::Name));
228
                }
229 230
            }
        }
231

232 233
        for (auto & pipe : pipes_from_dst)
            pipe.addTableLock(destination_lock);
234
    }
235

236 237
    Pipes pipes_from_buffers;
    pipes_from_buffers.reserve(num_shards);
238
    for (auto & buf : buffers)
239
        pipes_from_buffers.emplace_back(std::make_shared<BufferSource>(column_names, buf, *this));
240

241 242 243 244
    /** If the sources from the table were processed before some non-initial stage of query execution,
      * then sources from the buffers must also be wrapped in the processing pipeline before the same stage.
      */
    if (processed_stage > QueryProcessingStage::FetchColumns)
245 246
        for (auto & pipe : pipes_from_buffers)
            pipe = InterpreterSelectQuery(query_info.query, context, std::move(pipe), SelectQueryOptions(processed_stage)).executeWithProcessors().getPipe();
247

248 249
    if (query_info.prewhere_info)
    {
250 251 252
        for (auto & pipe : pipes_from_buffers)
            pipe.addSimpleTransform(std::make_shared<FilterTransform>(pipe.getHeader(), query_info.prewhere_info->prewhere_actions,
                    query_info.prewhere_info->prewhere_column_name, query_info.prewhere_info->remove_prewhere_column));
253 254 255

        if (query_info.prewhere_info->alias_actions)
        {
256 257
            for (auto & pipe : pipes_from_buffers)
                pipe.addSimpleTransform(std::make_shared<ExpressionTransform>(pipe.getHeader(), query_info.prewhere_info->alias_actions));
258 259 260 261

        }
    }

262 263 264 265
    for (auto & pipe : pipes_from_buffers)
        pipes_from_dst.emplace_back(std::move(pipe));

    return pipes_from_dst;
266 267 268
}


269 270
static void appendBlock(const Block & from, Block & to)
{
271 272 273
    if (!to)
        throw Exception("Cannot append to empty block", ErrorCodes::LOGICAL_ERROR);

274
    assertBlocksHaveEqualStructure(from, to, "Buffer");
275

276 277 278 279 280 281 282 283 284 285 286
    from.checkNumberOfRows();
    to.checkNumberOfRows();

    size_t rows = from.rows();
    size_t bytes = from.bytes();

    CurrentMetrics::add(CurrentMetrics::StorageBufferRows, rows);
    CurrentMetrics::add(CurrentMetrics::StorageBufferBytes, bytes);

    size_t old_rows = to.rows();

287 288
    auto temporarily_disable_memory_tracker = getCurrentMemoryTrackerActionLock();

289 290 291 292
    try
    {
        for (size_t column_no = 0, columns = to.columns(); column_no < columns; ++column_no)
        {
293
            const IColumn & col_from = *from.getByPosition(column_no).column.get();
294
            MutableColumnPtr col_to = (*std::move(to.getByPosition(column_no).column)).mutate();
295

296
            col_to->insertRangeFrom(col_from, 0, rows);
297

298
            to.getByPosition(column_no).column = std::move(col_to);
299 300 301 302 303 304 305 306 307
        }
    }
    catch (...)
    {
        /// Rollback changes.
        try
        {
            for (size_t column_no = 0, columns = to.columns(); column_no < columns; ++column_no)
            {
308
                ColumnPtr & col_to = to.getByPosition(column_no).column;
309
                if (col_to->size() != old_rows)
310
                    col_to = (*std::move(col_to)).mutate()->cut(0, old_rows);
311 312 313 314 315 316 317 318 319 320
            }
        }
        catch (...)
        {
            /// In case when we cannot rollback, do not leave incorrect state in memory.
            std::terminate();
        }

        throw;
    }
321 322 323
}


324 325 326
class BufferBlockOutputStream : public IBlockOutputStream
{
public:
327
    explicit BufferBlockOutputStream(StorageBuffer & storage_) : storage(storage_) {}
328 329

    Block getHeader() const override { return storage.getSampleBlock(); }
330 331 332 333 334 335

    void write(const Block & block) override
    {
        if (!block)
            return;

336 337 338
        // Check table structure.
        storage.check(block, true);

339 340 341 342 343
        size_t rows = block.rows();
        if (!rows)
            return;

        StoragePtr destination;
344
        if (storage.destination_id)
345
        {
346
            destination = DatabaseCatalog::instance().tryGetTable(storage.destination_id);
347 348
            if (destination.get() == &storage)
                throw Exception("Destination table is myself. Write will cause infinite loop.", ErrorCodes::INFINITE_LOOP);
349 350 351 352 353 354 355
        }

        size_t bytes = block.bytes();

        /// If the block already exceeds the maximum limit, then we skip the buffer.
        if (rows > storage.max_thresholds.rows || bytes > storage.max_thresholds.bytes)
        {
356
            if (storage.destination_id)
357 358 359
            {
                LOG_TRACE(storage.log, "Writing block with " << rows << " rows, " << bytes << " bytes directly.");
                storage.writeBlockToDestination(block, destination);
360
            }
361 362 363 364
            return;
        }

        /// We distribute the load on the shards by the stream number.
A
Alexey Milovidov 已提交
365
        const auto start_shard_num = getThreadId() % storage.num_shards;
366 367 368 369 370 371 372 373 374 375

        /// We loop through the buffers, trying to lock mutex. No more than one lap.
        auto shard_num = start_shard_num;

        StorageBuffer::Buffer * least_busy_buffer = nullptr;
        std::unique_lock<std::mutex> least_busy_lock;
        size_t least_busy_shard_rows = 0;

        for (size_t try_no = 0; try_no < storage.num_shards; ++try_no)
        {
A
Alexey Milovidov 已提交
376
            std::unique_lock lock(storage.buffers[shard_num].mutex, std::try_to_lock);
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393

            if (lock.owns_lock())
            {
                size_t num_rows = storage.buffers[shard_num].data.rows();
                if (!least_busy_buffer || num_rows < least_busy_shard_rows)
                {
                    least_busy_buffer = &storage.buffers[shard_num];
                    least_busy_lock = std::move(lock);
                    least_busy_shard_rows = num_rows;
                }
            }

            shard_num = (shard_num + 1) % storage.num_shards;
        }

        /// If you still can not lock anything at once, then we'll wait on mutex.
        if (!least_busy_buffer)
A
Amos Bird 已提交
394 395
        {
            least_busy_buffer = &storage.buffers[start_shard_num];
A
Alexey Milovidov 已提交
396
            least_busy_lock = std::unique_lock(least_busy_buffer->mutex);
A
Amos Bird 已提交
397 398
        }
        insertIntoBuffer(block, *least_busy_buffer);
399 400 401
        least_busy_lock.unlock();

        storage.reschedule();
402
    }
403
private:
404 405
    StorageBuffer & storage;

A
Amos Bird 已提交
406
    void insertIntoBuffer(const Block & block, StorageBuffer::Buffer & buffer)
407
    {
408
        time_t current_time = time(nullptr);
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423

        /// Sort the columns in the block. This is necessary to make it easier to concatenate the blocks later.
        Block sorted_block = block.sortColumns();

        if (!buffer.data)
        {
            buffer.data = sorted_block.cloneEmpty();
        }
        else if (storage.checkThresholds(buffer, current_time, sorted_block.rows(), sorted_block.bytes()))
        {
            /** If, after inserting the buffer, the constraints are exceeded, then we will reset the buffer.
              * This also protects against unlimited consumption of RAM, since if it is impossible to write to the table,
              *  an exception will be thrown, and new data will not be added to the buffer.
              */

424
            storage.flushBuffer(buffer, false /* check_thresholds */, true /* locked */);
425 426 427 428 429 430 431
        }

        if (!buffer.first_write_time)
            buffer.first_write_time = current_time;

        appendBlock(sorted_block, buffer.data);
    }
432 433 434
};


435
BlockOutputStreamPtr StorageBuffer::write(const ASTPtr & /*query*/, const Context & /*context*/)
436
{
437
    return std::make_shared<BufferBlockOutputStream>(*this);
438 439 440
}


441
bool StorageBuffer::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context & query_context) const
A
Alexey Milovidov 已提交
442
{
443
    if (!destination_id)
A
Alexey Milovidov 已提交
444 445
        return false;

446
    auto destination = DatabaseCatalog::instance().getTable(destination_id);
A
Alexey Milovidov 已提交
447 448 449 450

    if (destination.get() == this)
        throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP);

451
    return destination->mayBenefitFromIndexForIn(left_in_operand, query_context);
A
Alexey Milovidov 已提交
452 453 454
}


455 456
void StorageBuffer::startup()
{
457
    if (global_context.getSettingsRef().readonly)
458 459
    {
        LOG_WARNING(log, "Storage " << getName() << " is run with readonly settings, it will not be able to insert data."
P
Pradeep Chhetri 已提交
460
            << " Set appropriate system_profile to fix this.");
461 462
    }

463 464 465

    flush_handle = bg_pool.createTask(log->name() + "/Bg", [this]{ flushBack(); });
    flush_handle->activateAndSchedule();
466 467 468
}


469 470
void StorageBuffer::shutdown()
{
471
    flush_handle->deactivate();
472 473 474

    try
    {
475
        optimize(nullptr /*query*/, {} /*partition*/, false /*final*/, false /*deduplicate*/, global_context);
476 477 478 479 480
    }
    catch (...)
    {
        tryLogCurrentException(__PRETTY_FUNCTION__);
    }
481 482 483
}


484 485 486 487 488 489 490 491 492 493
/** NOTE If you do OPTIMIZE after insertion,
  * it does not guarantee, that all data will be in destination table at the time of next SELECT just after OPTIMIZE.
  *
  * Because in case if there was already running flushBuffer method,
  *  then call to flushBuffer inside OPTIMIZE will see empty buffer and return quickly,
  *  but at the same time, the already running flushBuffer method possibly is not finished,
  *  so next SELECT will observe missing data.
  *
  * This kind of race condition make very hard to implement proper tests.
  */
A
Alexey Milovidov 已提交
494
bool StorageBuffer::optimize(const ASTPtr & /*query*/, const ASTPtr & partition, bool final, bool deduplicate, const Context & /*context*/)
495
{
496
    if (partition)
497
        throw Exception("Partition cannot be specified when optimizing table of type Buffer", ErrorCodes::NOT_IMPLEMENTED);
498

499 500
    if (final)
        throw Exception("FINAL cannot be specified when optimizing table of type Buffer", ErrorCodes::NOT_IMPLEMENTED);
501

502 503 504
    if (deduplicate)
        throw Exception("DEDUPLICATE cannot be specified when optimizing table of type Buffer", ErrorCodes::NOT_IMPLEMENTED);

505 506
    flushAllBuffers(false);
    return true;
507 508 509
}


510
bool StorageBuffer::checkThresholds(const Buffer & buffer, time_t current_time, size_t additional_rows, size_t additional_bytes) const
511
{
512 513 514
    time_t time_passed = 0;
    if (buffer.first_write_time)
        time_passed = current_time - buffer.first_write_time;
515

516 517
    size_t rows = buffer.data.rows() + additional_rows;
    size_t bytes = buffer.data.bytes() + additional_bytes;
518

519
    return checkThresholdsImpl(rows, bytes, time_passed);
520 521 522 523 524
}


bool StorageBuffer::checkThresholdsImpl(size_t rows, size_t bytes, time_t time_passed) const
{
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
    if (time_passed > min_thresholds.time && rows > min_thresholds.rows && bytes > min_thresholds.bytes)
    {
        ProfileEvents::increment(ProfileEvents::StorageBufferPassedAllMinThresholds);
        return true;
    }

    if (time_passed > max_thresholds.time)
    {
        ProfileEvents::increment(ProfileEvents::StorageBufferPassedTimeMaxThreshold);
        return true;
    }

    if (rows > max_thresholds.rows)
    {
        ProfileEvents::increment(ProfileEvents::StorageBufferPassedRowsMaxThreshold);
        return true;
    }

    if (bytes > max_thresholds.bytes)
    {
        ProfileEvents::increment(ProfileEvents::StorageBufferPassedBytesMaxThreshold);
        return true;
    }

    return false;
550 551 552
}


553 554
void StorageBuffer::flushAllBuffers(const bool check_thresholds)
{
555 556
    for (auto & buf : buffers)
        flushBuffer(buf, check_thresholds);
557 558 559
}


A
Amos Bird 已提交
560
void StorageBuffer::flushBuffer(Buffer & buffer, bool check_thresholds, bool locked)
561
{
562
    Block block_to_write;
563
    time_t current_time = time(nullptr);
564 565 566 567 568

    size_t rows = 0;
    size_t bytes = 0;
    time_t time_passed = 0;

A
Alexey Milovidov 已提交
569
    std::unique_lock lock(buffer.mutex, std::defer_lock);
A
Amos Bird 已提交
570 571
    if (!locked)
        lock.lock();
572

573
    block_to_write = buffer.data.cloneEmpty();
574

575 576 577 578
    rows = buffer.data.rows();
    bytes = buffer.data.bytes();
    if (buffer.first_write_time)
        time_passed = current_time - buffer.first_write_time;
579

580 581 582 583 584 585 586 587 588 589
    if (check_thresholds)
    {
        if (!checkThresholdsImpl(rows, bytes, time_passed))
            return;
    }
    else
    {
        if (rows == 0)
            return;
    }
590

591 592
    buffer.data.swap(block_to_write);
    buffer.first_write_time = 0;
593

594 595
    CurrentMetrics::sub(CurrentMetrics::StorageBufferRows, block_to_write.rows());
    CurrentMetrics::sub(CurrentMetrics::StorageBufferBytes, block_to_write.bytes());
596

597
    ProfileEvents::increment(ProfileEvents::StorageBufferFlush);
598

599
    LOG_TRACE(log, "Flushing buffer with " << rows << " rows, " << bytes << " bytes, age " << time_passed << " seconds " << (check_thresholds ? "(bg)" : "(direct)") << ".");
600

601
    if (!destination_id)
602
        return;
603

604
    /** For simplicity, buffer is locked during write.
A
Alexey Milovidov 已提交
605
        * We could unlock buffer temporary, but it would lead to too many difficulties:
606 607 608 609 610 611
        * - data, that is written, will not be visible for SELECTs;
        * - new data could be appended to buffer, and in case of exception, we must merge it with old data, that has not been written;
        * - this could lead to infinite memory growth.
        */
    try
    {
612
        writeBlockToDestination(block_to_write, DatabaseCatalog::instance().tryGetTable(destination_id));
613 614 615 616
    }
    catch (...)
    {
        ProfileEvents::increment(ProfileEvents::StorageBufferErrorOnFlush);
617

618
        /// Return the block to its place in the buffer.
619

620 621
        CurrentMetrics::add(CurrentMetrics::StorageBufferRows, block_to_write.rows());
        CurrentMetrics::add(CurrentMetrics::StorageBufferBytes, block_to_write.bytes());
622

623
        buffer.data.swap(block_to_write);
624

625 626
        if (!buffer.first_write_time)
            buffer.first_write_time = current_time;
627

628 629
        /// After a while, the next write attempt will happen.
        throw;
630
    }
631 632
}

633

634 635
void StorageBuffer::writeBlockToDestination(const Block & block, StoragePtr table)
{
636
    if (!destination_id || !block)
637 638 639 640
        return;

    if (!table)
    {
641
        LOG_ERROR(log, "Destination table " << destination_id.getNameForLogs() << " doesn't exist. Block of data is discarded.");
642 643 644
        return;
    }

645 646
    auto temporarily_disable_memory_tracker = getCurrentMemoryTrackerActionLock();

647
    auto insert = std::make_shared<ASTInsertQuery>();
648
    insert->table_id = destination_id;
649 650 651 652

    /** We will insert columns that are the intersection set of columns of the buffer table and the subordinate table.
      * This will support some of the cases (but not all) when the table structure does not match.
      */
653
    Block structure_of_destination_table = allow_materialized ? table->getSampleBlock() : table->getSampleBlockNonMaterialized();
654
    Block block_to_write;
655 656 657 658 659
    for (size_t i : ext::range(0, structure_of_destination_table.columns()))
    {
        auto dst_col = structure_of_destination_table.getByPosition(i);
        if (block.has(dst_col.name))
        {
660 661
            auto column = block.getByName(dst_col.name);
            if (!column.type->equals(*dst_col.type))
662
            {
663
                LOG_WARNING(log, "Destination table " << destination_id.getNameForLogs()
A
alexey-milovidov 已提交
664
                    << " have different type of column " << backQuoteIfNeed(column.name) << " ("
665
                    << dst_col.type->getName() << " != " << column.type->getName()
666
                    << "). Block of data is converted.");
A
Alexey Milovidov 已提交
667
                column.column = castColumn(column, dst_col.type);
668
                column.type = dst_col.type;
669 670
            }

671
            block_to_write.insert(column);
672 673 674
        }
    }

675
    if (block_to_write.columns() == 0)
676
    {
677
        LOG_ERROR(log, "Destination table " << destination_id.getNameForLogs()
A
alexey-milovidov 已提交
678
            << " have no common columns with block in buffer. Block of data is discarded.");
679 680 681
        return;
    }

682
    if (block_to_write.columns() != block.columns())
683
        LOG_WARNING(log, "Not all columns from block in buffer exist in destination table "
684
            << destination_id.getNameForLogs() << ". Some columns are discarded.");
685 686 687

    auto list_of_columns = std::make_shared<ASTExpressionList>();
    insert->columns = list_of_columns;
688
    list_of_columns->children.reserve(block_to_write.columns());
A
alexey-milovidov 已提交
689
    for (const auto & column : block_to_write)
690
        list_of_columns->children.push_back(std::make_shared<ASTIdentifier>(column.name));
691

692
    InterpreterInsertQuery interpreter{insert, global_context, allow_materialized};
693 694 695

    auto block_io = interpreter.execute();
    block_io.out->writePrefix();
A
Alexey Milovidov 已提交
696
    block_io.out->write(block_to_write);
697
    block_io.out->writeSuffix();
698 699 700
}


701
void StorageBuffer::flushBack()
702
{
703 704 705 706 707 708 709 710
    try
    {
        flushAllBuffers(true);
    }
    catch (...)
    {
        tryLogCurrentException(__PRETTY_FUNCTION__);
    }
711

712 713 714 715 716 717 718 719 720
    reschedule();
}

void StorageBuffer::reschedule()
{
    time_t min_first_write_time = std::numeric_limits<time_t>::max();
    time_t rows = 0;

    for (auto & buffer : buffers)
721
    {
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
        std::lock_guard lock(buffer.mutex);
        min_first_write_time = buffer.first_write_time;
        rows += buffer.data.rows();
    }

    /// will be rescheduled via INSERT
    if (!rows)
        return;

    time_t current_time = time(nullptr);
    time_t time_passed = current_time - min_first_write_time;

    size_t min = std::max<ssize_t>(min_thresholds.time - time_passed, 1);
    size_t max = std::max<ssize_t>(max_thresholds.time - time_passed, 1);
    flush_handle->scheduleAfter(std::min(min, max) * 1000);
737 738
}

A
alesapin 已提交
739 740 741 742 743 744 745 746 747 748 749 750
void StorageBuffer::checkAlterIsPossible(const AlterCommands & commands, const Settings & /* settings */)
{
    for (const auto & command : commands)
    {
        if (command.type != AlterCommand::Type::ADD_COLUMN && command.type != AlterCommand::Type::MODIFY_COLUMN
            && command.type != AlterCommand::Type::DROP_COLUMN && command.type != AlterCommand::Type::COMMENT_COLUMN)
            throw Exception(
                "Alter of type '" + alterTypeToString(command.type) + "' is not supported by storage " + getName(),
                ErrorCodes::NOT_IMPLEMENTED);
    }
}

751 752 753 754 755 756 757 758 759 760 761
std::optional<UInt64> StorageBuffer::totalRows() const
{
    std::optional<UInt64> underlying_rows;
    auto underlying = DatabaseCatalog::instance().tryGetTable(destination_id);

    if (underlying)
        underlying_rows = underlying->totalRows();
    if (!underlying_rows)
        return underlying_rows;

    UInt64 rows = 0;
A
Alexey Milovidov 已提交
762
    for (const auto & buffer : buffers)
763 764 765 766 767 768
    {
        std::lock_guard lock(buffer.mutex);
        rows += buffer.data.rows();
    }
    return rows + *underlying_rows;
}
769

770 771 772
std::optional<UInt64> StorageBuffer::totalBytes() const
{
    UInt64 bytes = 0;
A
Alexey Milovidov 已提交
773
    for (const auto & buffer : buffers)
774 775 776 777 778 779 780
    {
        std::lock_guard lock(buffer.mutex);
        bytes += buffer.data.bytes();
    }
    return bytes;
}

781
void StorageBuffer::alter(const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder)
782
{
783
    lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout);
784

785
    auto table_id = getStorageID();
A
alesapin 已提交
786
    checkAlterIsPossible(params, context.getSettingsRef());
787

788
    /// So that no blocks of the old structure remain.
789
    optimize({} /*query*/, {} /*partition_id*/, false /*final*/, false /*deduplicate*/, context);
790

A
alesapin 已提交
791 792
    StorageInMemoryMetadata metadata = getInMemoryMetadata();
    params.apply(metadata);
A
fix  
Alexander Tokmakov 已提交
793
    DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, metadata);
A
alesapin 已提交
794
    setColumns(std::move(metadata.columns));
795 796
}

797 798 799 800 801 802 803 804 805 806

void registerStorageBuffer(StorageFactory & factory)
{
    /** Buffer(db, table, num_buckets, min_time, max_time, min_rows, max_rows, min_bytes, max_bytes)
      *
      * db, table - in which table to put data from buffer.
      * num_buckets - level of parallelism.
      * min_time, max_time, min_rows, max_rows, min_bytes, max_bytes - conditions for flushing the buffer.
      */

807
    factory.registerStorage("Buffer", [](const StorageFactory::Arguments & args)
808
    {
809 810 811
        ASTs & engine_args = args.engine_args;

        if (engine_args.size() != 9)
812 813 814 815
            throw Exception("Storage Buffer requires 9 parameters: "
                " destination_database, destination_table, num_buckets, min_time, max_time, min_rows, max_rows, min_bytes, max_bytes.",
                ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);

816
        engine_args[0] = evaluateConstantExpressionForDatabaseName(engine_args[0], args.local_context);
817
        engine_args[1] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[1], args.local_context);
818

I
Ivan Lezhankin 已提交
819 820
        String destination_database = engine_args[0]->as<ASTLiteral &>().value.safeGet<String>();
        String destination_table = engine_args[1]->as<ASTLiteral &>().value.safeGet<String>();
821

I
Ivan Lezhankin 已提交
822
        UInt64 num_buckets = applyVisitor(FieldVisitorConvertToNumber<UInt64>(), engine_args[2]->as<ASTLiteral &>().value);
823

I
Ivan Lezhankin 已提交
824 825 826 827 828 829
        Int64 min_time = applyVisitor(FieldVisitorConvertToNumber<Int64>(), engine_args[3]->as<ASTLiteral &>().value);
        Int64 max_time = applyVisitor(FieldVisitorConvertToNumber<Int64>(), engine_args[4]->as<ASTLiteral &>().value);
        UInt64 min_rows = applyVisitor(FieldVisitorConvertToNumber<UInt64>(), engine_args[5]->as<ASTLiteral &>().value);
        UInt64 max_rows = applyVisitor(FieldVisitorConvertToNumber<UInt64>(), engine_args[6]->as<ASTLiteral &>().value);
        UInt64 min_bytes = applyVisitor(FieldVisitorConvertToNumber<UInt64>(), engine_args[7]->as<ASTLiteral &>().value);
        UInt64 max_bytes = applyVisitor(FieldVisitorConvertToNumber<UInt64>(), engine_args[8]->as<ASTLiteral &>().value);
830

831 832 833 834 835 836 837 838
        /// If destination_id is not set, do not write data from the buffer, but simply empty the buffer.
        StorageID destination_id = StorageID::createEmpty();
        if (!destination_table.empty())
        {
            destination_id.database_name = args.context.resolveDatabase(destination_database);
            destination_id.table_name = destination_table;
        }

839
        return StorageBuffer::create(
840 841 842
            args.table_id,
            args.columns,
            args.constraints,
843
            args.context,
844 845 846
            num_buckets,
            StorageBuffer::Thresholds{min_time, min_rows, min_bytes},
            StorageBuffer::Thresholds{max_time, max_rows, max_bytes},
847
            destination_id,
Z
zhang2014 已提交
848
            static_cast<bool>(args.local_context.getSettingsRef().insert_allow_materialized_columns));
849 850 851
    });
}

852
}