|
6 | 6 | #include <ydb/core/persqueue/events/global.h>
|
7 | 7 | #include <ydb/core/persqueue/user_info.h>
|
8 | 8 | #include <ydb/core/persqueue/write_meta.h>
|
| 9 | +#include <ydb/core/testlib/actors/block_events.h> |
9 | 10 | #include <ydb/core/tx/scheme_board/events.h>
|
10 | 11 | #include <ydb/core/tx/scheme_board/events_internal.h>
|
11 | 12 | #include <ydb/public/sdk/cpp/client/ydb_datastreams/datastreams.h>
|
@@ -2065,17 +2066,25 @@ Y_UNIT_TEST_SUITE(Cdc) {
|
2065 | 2066 | return result;
|
2066 | 2067 | }
|
2067 | 2068 |
|
2068 |
| - void WaitForContent(TServer::TPtr server, const TActorId& sender, const TString& path, const TVector<TString>& expected) { |
| 2069 | + TVector<NJson::TJsonValue> WaitForContent(TServer::TPtr server, const TActorId& sender, const TString& path, const TVector<TString>& expected) { |
2069 | 2070 | while (true) {
|
2070 | 2071 | const auto records = GetRecords(*server->GetRuntime(), sender, path, 0);
|
| 2072 | + for (size_t i = 0; i < records.size(); ++i) { |
| 2073 | + Cerr << "... " << i << ": " << records[i].second << Endl; |
| 2074 | + } |
2071 | 2075 | for (ui32 i = 0; i < std::min(records.size(), expected.size()); ++i) {
|
2072 | 2076 | AssertJsonsEqual(records.at(i).second, expected.at(i));
|
2073 | 2077 | }
|
2074 | 2078 |
|
2075 | 2079 | if (records.size() >= expected.size()) {
|
2076 | 2080 | UNIT_ASSERT_VALUES_EQUAL_C(records.size(), expected.size(),
|
2077 | 2081 | "Unexpected record: " << records.at(expected.size()).second);
|
2078 |
| - break; |
| 2082 | + TVector<NJson::TJsonValue> values; |
| 2083 | + for (const auto& pr : records) { |
| 2084 | + bool ok = NJson::ReadJsonTree(pr.second, &values.emplace_back()); |
| 2085 | + Y_ABORT_UNLESS(ok); |
| 2086 | + } |
| 2087 | + return values; |
2079 | 2088 | }
|
2080 | 2089 |
|
2081 | 2090 | SimulateSleep(server, TDuration::Seconds(1));
|
@@ -3779,6 +3788,141 @@ Y_UNIT_TEST_SUITE(Cdc) {
|
3779 | 3788 | });
|
3780 | 3789 | }
|
3781 | 3790 |
|
| 3791 | + Y_UNIT_TEST(ResolvedTimestampForDisplacedUpsert) { |
| 3792 | + TPortManager portManager; |
| 3793 | + TServer::TPtr server = new TServer(TServerSettings(portManager.GetPort(2134), {}, DefaultPQConfig()) |
| 3794 | + .SetUseRealThreads(false) |
| 3795 | + .SetDomainName("Root") |
| 3796 | + ); |
| 3797 | + |
| 3798 | + TDisableDataShardLogBatching disableDataShardLogBatching; |
| 3799 | + |
| 3800 | + auto& runtime = *server->GetRuntime(); |
| 3801 | + const auto edgeActor = runtime.AllocateEdgeActor(); |
| 3802 | + |
| 3803 | + SetupLogging(runtime); |
| 3804 | + InitRoot(server, edgeActor); |
| 3805 | + SetSplitMergePartCountLimit(&runtime, -1); |
| 3806 | + CreateShardedTable(server, edgeActor, "/Root", "Table", SimpleTable()); |
| 3807 | + |
| 3808 | + WaitTxNotification(server, edgeActor, AsyncAlterAddStream(server, "/Root", "Table", |
| 3809 | + WithVirtualTimestamps(WithResolvedTimestamps( |
| 3810 | + TDuration::Seconds(3), Updates(NKikimrSchemeOp::ECdcStreamFormatJson))))); |
| 3811 | + |
| 3812 | + Cerr << "... prepare" << Endl; |
| 3813 | + WaitForContent(server, edgeActor, "/Root/Table/Stream", { |
| 3814 | + R"({"resolved":"***"})", |
| 3815 | + }); |
| 3816 | + |
| 3817 | + KqpSimpleExec(runtime, R"( |
| 3818 | + UPSERT INTO `/Root/Table` (key, value) VALUES (1, 10); |
| 3819 | + )"); |
| 3820 | + |
| 3821 | + auto records = WaitForContent(server, edgeActor, "/Root/Table/Stream", { |
| 3822 | + R"({"resolved":"***"})", |
| 3823 | + R"({"update":{"value":10},"key":[1],"ts":"***"})", |
| 3824 | + R"({"resolved":"***"})", |
| 3825 | + }); |
| 3826 | + |
| 3827 | + // Take the final step |
| 3828 | + ui64 lastStep = records.back()["resolved"][0].GetUInteger(); |
| 3829 | + Cerr << "... last heartbeat at " << lastStep << Endl; |
| 3830 | + |
| 3831 | + const auto tableId = ResolveTableId(server, edgeActor, "/Root/Table"); |
| 3832 | + const auto shards = GetTableShards(server, edgeActor, "/Root/Table"); |
| 3833 | + UNIT_ASSERT_VALUES_EQUAL(shards.size(), 1u); |
| 3834 | + |
| 3835 | + ui64 coordinator = ChangeStateStorage(Coordinator, server->GetSettings().Domain); |
| 3836 | + ui64 snapshotStep = lastStep + 3000 - 1; |
| 3837 | + ForwardToTablet(runtime, coordinator, edgeActor, new TEvTxProxy::TEvRequirePlanSteps(coordinator, snapshotStep)); |
| 3838 | + |
| 3839 | + TBlockEvents<TEvMediatorTimecast::TEvGranularUpdate> blockedGranularUpdates(runtime, |
| 3840 | + [&](auto& ev) { |
| 3841 | + return ev->Get()->Record.GetLatestStep() > snapshotStep; |
| 3842 | + }); |
| 3843 | + TBlockEvents<TEvMediatorTimecast::TEvUpdate> blockedUpdates(runtime, |
| 3844 | + [&](auto& ev) { |
| 3845 | + return ev->Get()->Record.GetTimeBarrier() > snapshotStep; |
| 3846 | + }); |
| 3847 | + |
| 3848 | + Cerr << "... performing a read from snapshot just before the next heartbeat" << Endl; |
| 3849 | + { |
| 3850 | + auto req = std::make_unique<TEvDataShard::TEvRead>(); |
| 3851 | + { |
| 3852 | + auto& record = req->Record; |
| 3853 | + record.SetReadId(1); |
| 3854 | + record.MutableTableId()->SetOwnerId(tableId.PathId.OwnerId); |
| 3855 | + record.MutableTableId()->SetTableId(tableId.PathId.LocalPathId); |
| 3856 | + record.AddColumns(1); |
| 3857 | + record.AddColumns(2); |
| 3858 | + record.SetResultFormat(NKikimrDataEvents::FORMAT_CELLVEC); |
| 3859 | + ui32 key = 1; |
| 3860 | + TVector<TCell> keys; |
| 3861 | + keys.push_back(TCell::Make(key)); |
| 3862 | + req->Keys.push_back(TSerializedCellVec(TSerializedCellVec::Serialize(keys))); |
| 3863 | + record.MutableSnapshot()->SetStep(snapshotStep); |
| 3864 | + record.MutableSnapshot()->SetTxId(Max<ui64>()); |
| 3865 | + } |
| 3866 | + ForwardToTablet(runtime, shards.at(0), edgeActor, req.release()); |
| 3867 | + auto ev = runtime.GrabEdgeEventRethrow<TEvDataShard::TEvReadResult>(edgeActor); |
| 3868 | + auto* res = ev->Get(); |
| 3869 | + UNIT_ASSERT_VALUES_EQUAL(res->Record.GetStatus().GetCode(), Ydb::StatusIds::SUCCESS); |
| 3870 | + UNIT_ASSERT_VALUES_EQUAL(res->Record.GetFinished(), true); |
| 3871 | + Cerr << "... read finished" << Endl; |
| 3872 | + } |
| 3873 | + for (int i = 0; i < 10; ++i) { |
| 3874 | + runtime.SimulateSleep(TDuration::MilliSeconds(1)); |
| 3875 | + } |
| 3876 | + |
| 3877 | + Cerr << "... starting upsert 1 (expected to displace)" << Endl; |
| 3878 | + auto upsert1 = KqpSimpleSend(runtime, R"( |
| 3879 | + UPSERT INTO `/Root/Table` (key, value) VALUES (2, 20); |
| 3880 | + )"); |
| 3881 | + for (int i = 0; i < 10; ++i) { |
| 3882 | + runtime.SimulateSleep(TDuration::MilliSeconds(1)); |
| 3883 | + } |
| 3884 | + |
| 3885 | + Cerr << "... starting upsert 2 (expected to displace)" << Endl; |
| 3886 | + auto upsert2 = KqpSimpleSend(runtime, R"( |
| 3887 | + UPSERT INTO `/Root/Table` (key, value) VALUES (3, 30); |
| 3888 | + )"); |
| 3889 | + for (int i = 0; i < 10; ++i) { |
| 3890 | + runtime.SimulateSleep(TDuration::MilliSeconds(1)); |
| 3891 | + } |
| 3892 | + |
| 3893 | + Cerr << "... unblocking updates" << Endl; |
| 3894 | + blockedGranularUpdates.Unblock().Stop(); |
| 3895 | + blockedUpdates.Unblock().Stop(); |
| 3896 | + for (int i = 0; i < 10; ++i) { |
| 3897 | + runtime.SimulateSleep(TDuration::MilliSeconds(1)); |
| 3898 | + } |
| 3899 | + |
| 3900 | + Cerr << "... checking the update is logged before the new resolved timestamp" << Endl; |
| 3901 | + records = WaitForContent(server, edgeActor, "/Root/Table/Stream", { |
| 3902 | + R"({"resolved":"***"})", |
| 3903 | + R"({"update":{"value":10},"key":[1],"ts":"***"})", |
| 3904 | + R"({"resolved":"***"})", |
| 3905 | + R"({"update":{"value":20},"key":[2],"ts":"***"})", |
| 3906 | + R"({"update":{"value":30},"key":[3],"ts":"***"})", |
| 3907 | + R"({"resolved":"***"})", |
| 3908 | + }); |
| 3909 | + |
| 3910 | + TRowVersion resolved(0, 0); |
| 3911 | + for (auto& record : records) { |
| 3912 | + if (record.Has("resolved")) { |
| 3913 | + resolved.Step = record["resolved"][0].GetUInteger(); |
| 3914 | + resolved.TxId = record["resolved"][1].GetUInteger(); |
| 3915 | + } |
| 3916 | + if (record.Has("ts")) { |
| 3917 | + TRowVersion ts( |
| 3918 | + record["ts"][0].GetUInteger(), |
| 3919 | + record["ts"][1].GetUInteger()); |
| 3920 | + UNIT_ASSERT_C(resolved < ts, |
| 3921 | + "Record with ts " << ts << " after resolved " << resolved); |
| 3922 | + } |
| 3923 | + } |
| 3924 | + } |
| 3925 | + |
3782 | 3926 | } // Cdc
|
3783 | 3927 |
|
3784 | 3928 | } // NKikimr
|
|
0 commit comments