Added audio cache for wav tracks#170
Conversation
| <localConnection poolSize="100" /> | ||
| <remoteConnection poolSize="50" /> | ||
| <player poolSize="50" /> | ||
| <player> |
There was a problem hiding this comment.
would it make sense to add an option to enable/disable caching?
|
@achikin , I've added some comments in your commit. Also, can you write test case to verify the cache well-functioning in the WavTrack class? Thank you for your contribution! |
|
i have made a few changes:
Could you review PR please? |
|
@hamsterksu no way around that lock on every access to the singleton cache? |
|
@hrosa |
| Cache<URL, byte[]> cache = getCache(); | ||
| Cache<URL, AudioStreamCache> cache = getCache(); | ||
|
|
||
| if (!cache.containsKey(uri)) { |
There was a problem hiding this comment.
doesn't cache support putIfAbsent? That would be preferred to locking. It should also be OK now since stream is now lazily loaded.
There was a problem hiding this comment.
yes, it's possible to replace the following code by putIfAbsent but i use it to avoid unnecessary objects creating
lock.lock();
try {
//need to check twice
if (!cache.containsKey(uri)) {
cache.put(uri, new AudioStreamCache(uri));
}
} finally {
lock.unlock();
}
There was a problem hiding this comment.
I'm really worried about bottleneck imposed by this lock. Let's remove it and use putIfAbsent.
We can workaround the object creation issue by recycling them using a pool. What do you think @hamsterksu ?
Object old = putIfAbsent(K, V)
if(old != null) then recycle(V)
| mockConnection = mock(URLConnection.class); | ||
|
|
||
| //we need use answer to return new stream each time | ||
| when(mockConnection.getInputStream()).thenAnswer(new Answer<InputStream>() { |
There was a problem hiding this comment.
when().thenReturn() form is preferred
There was a problem hiding this comment.
we need to use this variant. otherwise answer will be cached and inputstream can't be read twice
|
Hi @hamsterksu can you pool the AudioStreamCache objects? I'm also thinking pool size should be configurable (via config file) so users can tailor it according to expected load on the server. |
|
@hrosa do we need to have pool for AudioStreamCache? it can be a bottleneck too. |
|
@hamsterksu I actually didn't notice that contains() there. Sorry about that. Thinking about it, I believe we have potential for null pointers as is. If map.contains() returns true while another thread is removing that object from map, then this line will result in NPE. A possible workaround is to use map.get(key): Object obj = map.get(key); I agree that this way, pools shouldn't be required. Some trash can still be generated, but not for every request and I'm confident G1 can handle that. Wdyt? |
|
I believe I found one issue, first call (for a new audio file that is not yet cached) always fails. The logs show that the audio file cannot be found, causing the Play signal to abort. An MGCP NFTY is sent immediately back to RestComm, so no audio can be heard.
Can you verify please? |
|
@hrosa sure i will check it, do you use separate instances of restcomm and mediaserver? |
|
@hamsterksu was testing locally, so RestComm and MS were running in same instance. |
|
hi @hrosa i have found issue
unfortunately i removed prev fork so i have created new, applied pull request as patch and made fix |

In the current implementation, Restcomm caches audio files in application server, while media server downloads them from application server every time it plays a file to a user. It's ok to do so when you keep app server and meida server on the same machine. As soon as we have split app and media server - we've added cache on mediaserver's side. We've used http://www.ehcache.org/ not to reinvent the wheel.