UDP is certainly faster than TCP, no question about it, because of different levels of quality of service. However, coding for both is similarly simple, given the same requirement for short receive-only message. TCP session has nothing to do with the complexity. The complexity comes in how to do process() in parallel.
A receive-only message can be handled in TCP:
server_socket = create_and_listen_on_socket(..., MAXCONN)
while True:
csock = server_sock.accept()
msg = csock.recv()
csock.close()
process(msg)
A UDP loop has similar steps, minus the accept and close. The complexity to use threads or async to run process() is the same for UDP and TCP.
For a normal data volume remote command protocol, the simple TCP loop is more than adequate. Just set a reasonable MAXCONN to queue up client connection requests, which can drop connections if there are too many requests, just like UDP dropping packets.
Edit: I don't believe lock can be avoided in Sonic Pi server when handling concurrent incoming commands, whether it's written in Erlang or not. Sonic Pi I believe has a single audio device, which makes it a shared resource. Concurrent access to a shared resource has to be managed with lock somewhere along the call path. In that case a single thread TCP server is perfectly fine, serving as a lock as well.