Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Source Cleanup: When there is a large amount of streaming, Source leakage causes OOM (Out of Memory). #1509

Closed
2k9laojia opened this issue Dec 10, 2019 · 19 comments
Assignees
Labels
Duplicated Duplicated bug. TransByAI Translated by AI/GPT.
Milestone

Comments

@2k9laojia
Copy link

2k9laojia commented Dec 10, 2019

Description
Describe the problem you encountered.
When pushing the stream to SRS, the SRS process crashed.

Environment

  1. Operating System: CentOS
  2. Encoder (Tool and Version): ...
  3. Player (Tool and Version): ...
  4. SRS Version: 2.0.248
  5. SRS log is as follows:
    client identified, type=fmle-publish, stream_name=62c1352ea3a64621999bfd946081f392, duration=-1.00
    connected stream, tcUrl=rtmp://172.16.129.97/netposa, pageUrl=, swfUrl=, schema=rtmp, vhost=defaultVhost, port=1935, app=netposa, stream=62c1352ea3a64621999bfd946081f392, args=null
    st_thread_create failed. ret=1017(Cannot allocate memory)

Reproduction
The steps to reproduce the bug are as follows:

  1. Start SRS, run ...
  2. Push the stream, run ...
  3. The bug is reproduced, and the key information is as follows:
...

Expected Behavior
Describe what you expect to happen.

By examining the stack analysis, the reasons are as follows:
During the push stream process, the function SrsSource::initialize is called, which then calls hls->initialize. However, an error occurs in hls->initialize, specifically st_thread_create failed, resulting in an error code being returned. In the function SrsSource::fetch_or_create, srs_freep(source) is called, leading to the destructor of SrsSource being invoked. As a result, the member variable play_edge is released, triggering its destructor and subsequently calling the destructor of the SrsPlayEdge member variable ingester. The stop function is called, ultimately invoking _source->on_unpublish(). Since the call to hls->initialize failed and returned without calling play_edge->initialize(this, _req), the _source pointer in SrsEdgeIngester is null, causing a core dump. Therefore, adding a null check before using _source can resolve the issue.

I hope someone knowledgeable can help answer why st_thread_create failed and why it resulted in a failure.

TRANS_BY_GPT3

@winlinvip
Copy link
Member

winlinvip commented Dec 10, 2019

The reason is clearly stated in the log: 'Cannot allocate memory'.
Can the scenario of running out of memory be reproduced?

TRANS_BY_GPT3

@2k9laojia
Copy link
Author

2k9laojia commented Dec 11, 2019

My server had 60GB of remaining memory at that time, and the current SRS process was using 3.4GB of memory. Does running out of memory refer to the memory limit for the current process? This issue was occurring repeatedly while handling 500 streaming routes, closing and restarting them.

TRANS_BY_GPT3

@2k9laojia
Copy link
Author


111

@2k9laojia
Copy link
Author

2k9laojia commented Dec 13, 2019

After checking the srsSource objects in the pool, there are SrsSource::do_cycle_all with a pool size of [38320]. I don't know if it has any impact or not.

TRANS_BY_GPT3

@winlinvip
Copy link
Member

winlinvip commented Dec 13, 2019

You have also used a considerable amount of memory for other things, so the available memory should be less than 60GB.

How did you test it? Can you describe the process?

Encoder (tool and version): ...
Player (tool and version): ...

TRANS_BY_GPT3

@2k9laojia
Copy link
Author

                total        used        free      shared  buff/cache   available

Mem: 109G 10G 61G 4.0G 38G 94G
Swap: 4.0G 250M 3.8G

@2k9laojia
Copy link
Author

2k9laojia commented Dec 13, 2019

I push the stream to SRS using route 500, then play it on 500 clients, repeat for one hour, close all streams, and repeat the above steps. Each stream has a unique stream ID, so a new SRS source object is created every time a stream is pushed. SRSlibrtmp is used for pushing the stream, and JMeter is used for playing the stream, which is discarded after obtaining it. Now that the stress test is over, the CPU and memory usage of SRS remain high. I plan to add pool cleaning in the reload function. When this situation occurs, I will send a signal to SRS to execute a reload and release all SRS source objects in the pool, to see if the CPU and memory usage can be reduced.

TRANS_BY_GPT3

@2k9laojia
Copy link
Author

2k9laojia commented Dec 13, 2019

WeChat Screenshot_20191213094616

TRANS_BY_GPT3

@2k9laojia
Copy link
Author

2k9laojia commented Dec 13, 2019

WeChat Screenshot_20191213095329'

Note: The translation provided maintains the markdown structure by italicizing the text.

TRANS_BY_GPT3

@2k9laojia
Copy link
Author

2k9laojia commented Dec 13, 2019

An srsSource object will start 2 coroutines. At that time, there were 38,320 remaining srsSource objects in the pool, which is equivalent to over 70,000 coroutines. This scheduling is a significant CPU consumption. After releasing the srsSource, the CPU usage can be noticeably reduced.

SRScpu memory

TRANS_BY_GPT3

@winlinvip
Copy link
Member

winlinvip commented Dec 13, 2019

Okay, I understand what you mean. It seems like the performance issue is caused by too many sources. I will try to reproduce it first. Thank you.

TRANS_BY_GPT3

@winlinvip winlinvip changed the title srs中hls初始化错误时,导致srs崩掉 大量推流时,Source泄漏导致OOM Jan 8, 2020
@winlinvip
Copy link
Member

winlinvip commented Jan 15, 2020

After I enabled HTTP FLV, I created a new source with curl and repeatedly used "killall curl" to reproduce this issue. The speed increases at around 1MB every 3 seconds.

for ((i=0;;i++)); do curl http://localhost:8080/live/livestream-$i.flv -o /dev/null ; sleep 0.1; done

for ((;;)); do killall curl; sleep 0.2; done

I discovered that many coroutines are actually SrsAsyncCallWorker, which means they are coroutines for asynchronous callbacks.

(gdb) p _st_iterate_threads_flag =1
(gdb) b _st_iterate_threads
(gdb) c
(gdb) bt
#1  0x000000000059ff55 in st_cond_timedwait (cvar=0x4953b40, 
    timeout=18446744073709551615) at sync.c:197
#2  0x00000000005a003a in st_cond_wait (cvar=0x4953b40) at sync.c:219
#3  0x00000000004aa3f6 in srs_cond_wait (cond=0x4953b40)
    at src/service/srs_service_st.cpp:334
#4  0x000000000058432c in SrsAsyncCallWorker::cycle (this=0x4953ae0)
    at src/app/srs_app_async_call.cpp:104
#5  0x00000000004f78da in SrsSTCoroutine::cycle (this=0x495a3c0)
    at src/app/srs_app_st.cpp:198
#6  0x00000000004f794f in SrsSTCoroutine::pfn (arg=0x495a3c0)
    at src/app/srs_app_st.cpp:213

I found that HLS and DVR are using this.

SrsDvrPlan::SrsDvrPlan() {
    async = new SrsAsyncCallWorker();

SrsHlsMuxer::SrsHlsMuxer() {
    async = new SrsAsyncCallWorker();

Running for about 16 minutes, with 4k streams, it occupies 125MB of memory and CPU usage is 5%.

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND     
21719 root      20   0 1091240 126256   4976 S   5.0  6.2   0:30.52 ./objs/srs+ 

real	16m19.259s
user	0m23.620s
sys	0m6.990s

If this part is optimized, the number of coroutines may decrease, but it will not have a significant impact on the overall performance.

TRANS_BY_GPT3

@winlinvip
Copy link
Member

winlinvip commented Jan 15, 2020

Modify HLS and DVR to only start SrsAsyncCallWorker when publishing and stop it when unpublishing. This will prevent a Source from having two persistent coroutines.

When there are 4k streams, the situation is slightly better for the empty Source test compared to before. The memory has decreased to 80MB, while the CPU remains at 5%.

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND     
21994 root      20   0  418252  83880   4992 S   5.0  4.1   0:27.98 ./objs/srs+ 

The number of coroutines has already been reduced to 8.

(gdb) p _st_active_count 
$2 = 8

We need to use a tool to check the reason.

TRANS_BY_GPT3

@winlinvip
Copy link
Member

winlinvip commented Jan 15, 2020

Enable SRS support for gperf and valgrind, refer to SRS Performance (CPU) and Memory Optimization Tool Usage:

./configure --with-gperf --with-gcp --with-gmc --with-gmp --with-valgrind

We found that CPU usage is high in the HTTP handler lookup. Since each stream source creates a new handler, the time taken to find the handler will also increase as the number of sources grows.

      34   1.5%  53.5%      320  14.1% SrsHttpServeMux::match

In GMP analysis, the main memory performance overhead is in:

   101.4  72.1%  72.1%    101.4  72.1% SrsFastVector::SrsFastVector

In Valgrind analysis, the memory leaks mainly occur in SrsFastVector.

==1426== 8,224,768 bytes in 1,004 blocks are still reachable in loss record 360 of 361
==1426==    at 0x4C2AB68: operator new[](unsigned long) (vg_replace_malloc.c:433)
==1426==    by 0x4CFA63: SrsFastVector::SrsFastVector() (srs_app_source.cpp:158)
==1426==    by 0x4CFE81: SrsMessageQueue::SrsMessageQueue(bool) (srs_app_source.cpp:241)
==1426==    by 0x563F74: SrsEdgeForwarder::SrsEdgeForwarder() (srs_app_edge.cpp:441)
==1426==    by 0x56575F: SrsPublishEdge::SrsPublishEdge() (srs_app_edge.cpp:721)
==1426==    by 0x4D6982: SrsSource::SrsSource() (srs_app_source.cpp:1769)
==1426==    by 0x4D63EE: SrsSource::fetch_or_create(SrsRequest*, ISrsSourceHandler*, SrsSource**) (srs_app_source.cpp:1656)
==1426==    by 0x4F2C8D: SrsHttpStreamServer::hijack(ISrsHttpMessage*, ISrsHttpHandler**) (srs_app_http_stream.cpp:1092)
==1426==    by 0x496626: SrsHttpServeMux::find_handler(ISrsHttpMessage*, ISrsHttpHandler**) (srs_http_stack.cpp:737)
==1426==    by 0x54CC13: SrsHttpServer::serve_http(ISrsHttpResponseWriter*, ISrsHttpMessage*) (srs_app_http_conn.cpp:278)
==1426==    by 0x497218: SrsHttpCorsMux::serve_http(ISrsHttpResponseWriter*, ISrsHttpMessage*) (srs_http_stack.cpp:859)
==1426==    by 0x54BE78: SrsHttpConn::process_request(ISrsHttpResponseWriter*, ISrsHttpMessage*) (srs_app_http_conn.cpp:161)
==1426== 
==1426== 8,224,768 bytes in 1,004 blocks are still reachable in loss record 361 of 361
==1426==    at 0x4C2AB68: operator new[](unsigned long) (vg_replace_malloc.c:433)
==1426==    by 0x4CFA63: SrsFastVector::SrsFastVector() (srs_app_source.cpp:158)
==1426==    by 0x4CFE81: SrsMessageQueue::SrsMessageQueue(bool) (srs_app_source.cpp:241)
==1426==    by 0x4ECCD1: SrsBufferCache::SrsBufferCache(SrsSource*, SrsRequest*) (srs_app_http_stream.cpp:63)
==1426==    by 0x4F19F3: SrsHttpStreamServer::http_mount(SrsSource*, SrsRequest*) (srs_app_http_stream.cpp:875)
==1426==    by 0x4F2D21: SrsHttpStreamServer::hijack(ISrsHttpMessage*, ISrsHttpHandler**) (srs_app_http_stream.cpp:1098)
==1426==    by 0x496626: SrsHttpServeMux::find_handler(ISrsHttpMessage*, ISrsHttpHandler**) (srs_http_stack.cpp:737)
==1426==    by 0x54CC13: SrsHttpServer::serve_http(ISrsHttpResponseWriter*, ISrsHttpMessage*) (srs_app_http_conn.cpp:278)
==1426==    by 0x497218: SrsHttpCorsMux::serve_http(ISrsHttpResponseWriter*, ISrsHttpMessage*) (srs_http_stack.cpp:859)
==1426==    by 0x54BE78: SrsHttpConn::process_request(ISrsHttpResponseWriter*, ISrsHttpMessage*) (srs_app_http_conn.cpp:161)
==1426==    by 0x54BB38: SrsHttpConn::do_cycle() (srs_app_http_conn.cpp:133)
==1426==    by 0x4C5617: SrsConnection::cycle() (srs_app_conn.cpp:171)

TRANS_BY_GPT3

@winlinvip
Copy link
Member

winlinvip commented Jan 15, 2020

When there are 4,000 streams, changing the default size of the fast vector from 8,000 to 8 bytes reduces the memory to 45MB, and the CPU usage is at 4.7%.

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND     
 2661 root      20   0  330548  45548   5024 R   4.7  2.2   0:24.26 ./objs/srs+ 

TRANS_BY_GPT3

@winlinvip
Copy link
Member

winlinvip commented Jan 19, 2020

Will be fixed in #1579 to support hot upgrade or smooth upgrade. Upgrade smoothly.

Dup to #1509
Solution: #1579 (comment)

TRANS_BY_GPT3

@l0g1n
Copy link

l0g1n commented Dec 27, 2020

A srsSource object will start 2 coroutines. At that time, there were 38,320 srsSource objects remaining in the pool, equivalent to over 70,000 coroutines. This scheduling is a significant CPU burden. After releasing the srsSource objects, the CPU usage can be noticeably reduced.
SRScpu内存

May I ask, how did you view the number of coroutines in the process with the command or tool you used to generate this graph?

TRANS_BY_GPT3

@winlinvip
Copy link
Member

winlinvip commented Sep 13, 2021

Dup to #413

@winlinvip winlinvip reopened this Sep 13, 2021
@winlinvip winlinvip added Bug It might be a bug. Enhancement Improvement or enhancement. labels Sep 13, 2021
@winlinvip winlinvip modified the milestones: 3.0, 5.0 Sep 13, 2021
@winlinvip winlinvip changed the title 大量推流时,Source泄漏导致OOM Source清理:大量推流时,Source泄漏导致OOM Sep 13, 2021
@winlinvip winlinvip added Duplicated Duplicated bug. and removed Bug It might be a bug. Enhancement Improvement or enhancement. labels Sep 13, 2021
@winlinvip winlinvip modified the milestones: 5.0, 4.0 Sep 13, 2021
@winlinvip winlinvip changed the title Source清理:大量推流时,Source泄漏导致OOM Source Cleanup: When there is a large amount of streaming, Source leakage causes OOM (Out of Memory). Jul 26, 2023
@winlinvip winlinvip added the TransByAI Translated by AI/GPT. label Jul 26, 2023
@winlinvip
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicated Duplicated bug. TransByAI Translated by AI/GPT.
Projects
None yet
Development

No branches or pull requests

3 participants