@@ -13,6 +13,8 @@ extern "C" {
1313#include < absl/strings/str_replace.h>
1414#include < gmock/gmock.h>
1515
16+ #include < thread>
17+
1618#include " base/gtest.h"
1719#include " base/logging.h"
1820
@@ -526,4 +528,68 @@ TEST_F(InterpreterTest, LuaIntOverflow) {
526528 EXPECT_FALSE (Execute (" EVAL \" struct.pack('>I2147483648', '10')\" 0" ));
527529}
528530
531+ TEST_F (InterpreterTest, LuaGcStatistic) {
532+ InterpreterManager im (1 );
533+ auto * interpreter = im.Get ();
534+
535+ std::string_view keys[] = {" key1" , " key2" , " key3" , " key4" , " key5" , " key6" , " key7" };
536+ interpreter->SetGlobalArray (" KEYS" , SliceSpan{keys});
537+
538+ auto cb = [](Interpreter::CallArgs ca) {
539+ auto * reply = ca.translator ;
540+ reply->OnInt (1 );
541+ };
542+ interpreter->SetRedisFunc (cb);
543+ // next script generate several big values and set them to the keys
544+ // after the script is finished, GM isn't called for all values and
545+ // in the most cases we have more than 300k allocated memory
546+ // that will be cleaned later in the separate thread
547+ std::string script = R"(
548+ for i = 1, 7 do
549+ local str = string.rep(i, 1024 * 100)
550+ redis.call('SET', KEYS[1], str .. str)
551+ end
552+ )" ;
553+
554+ char sha_buf[64 ];
555+ Interpreter::FuncSha1 (script, sha_buf);
556+ string_view sha{sha_buf, std::strlen (sha_buf)};
557+
558+ string result;
559+ Interpreter::AddResult add_res = interpreter->AddFunction (sha, script, &result);
560+ EXPECT_EQ (Interpreter::ADD_OK, add_res);
561+
562+ // When script is executed in the most cases we see that not all memory was deallocated
563+ // immediately and can be deallocated later
564+ Interpreter::RunResult run_res = interpreter->RunFunction (sha, &error_);
565+ EXPECT_EQ (Interpreter::RUN_OK, run_res);
566+
567+ // check that after script is finished not the all memory was deallocated
568+ uint64_t used_bytes = InterpreterManager::tl_stats ().used_bytes ;
569+ EXPECT_GE (used_bytes, 0 );
570+
571+ auto force_gc_calls = InterpreterManager::tl_stats ().force_gc_calls ;
572+ // we need return interpreter to update statistic
573+ // force_gc_calls shouldn't be called
574+ im.Return (interpreter);
575+ EXPECT_EQ (force_gc_calls, InterpreterManager::tl_stats ().force_gc_calls );
576+ EXPECT_LE (used_bytes, InterpreterManager::tl_stats ().used_bytes );
577+
578+ used_bytes = InterpreterManager::tl_stats ().used_bytes ;
579+
580+ // we get the same interpeter again to call GC in separate thread
581+ auto * new_interpreter = im.Get ();
582+ EXPECT_EQ (interpreter, new_interpreter);
583+
584+ // check that even if memory is deallocated in separate thread our statistic is correct
585+ std::thread t ([&] {
586+ interpreter->RunGC ();
587+ EXPECT_EQ (InterpreterManager::tl_stats ().used_bytes , 0 );
588+ });
589+ t.join ();
590+
591+ im.Return (interpreter);
592+ EXPECT_GE (used_bytes, InterpreterManager::tl_stats ().used_bytes );
593+ }
594+
529595} // namespace dfly
0 commit comments