Vou primeiro passar pelas soluções que vc mesmo propôs:
-
criar um state para cada requisição que vai morrer no fim dela: a única parte "horrível" que consigo ver aqui é o desempenho que seria impactado significativamente por ter de criar e carregar o lua state a cada requisição. Algo que pode aliviar isso seria manter um pool de estados em branco pré-criados que só são consumidos quando há requisições. Este pool poderia ser preenchido constantemente em uma thread separada, isso aliviaria mais ainda o problema de desempenho e acredito que não teria problemas com threads, uma vez que ele nunca é usado simultaneamente em dois threads diferentes.
-
outra solução é manter um pool de estados: Bem eu devia ter lido esse antes de escrever meu ponto acima :) Mas acho que a diferença é que vc não precisa separar por threads. Não importa em qual thread o lua state é criado, mesmo que seja utilizado em outro thread. Isso não acarreta em problemas de multithreading pois ele nunca é usado ao mesmo tempo em dois threads. Quanto à solução opensource que utilize essa abordagem: também não sei dizer.
-
por último, bolar um event loop para tornar o call de cada script assíncrona: não conheço a linguagem Rust pra poder afirmar, mas imagino que vc teria que implementar todo o core de event loop e async em todos lugares que fazem I/O. Me parece extremamente complexo. Eu sei que existe o Tokio (https://tokio.rs/) para Rust, mas como nunca usei não posso dizer muito; talvez ele te ajude aqui, mas talvez vc precisaria re-implementar toda biblioteca de I/O do Lua, pra que leve em conta o assincronismo e isso me parece um trabalho absurdamente gigante!
Agora deixa eu questionar a primeira solução que vc deu e que eu complementei. Por que o state precisa morrer ao final da requisição? Não poderia ele ser long-lived, isto é, servir várias requisições no decorrer da vida dele, sem que precise morrer e ser re-criado a cada requisição? Isso é um requisito que vc tem que definir, mas estou só questionando o porquê dele. Se vc quer garantir absolutamente que não haverá estado compartilhado entre uma requisição e outra, então faz sentido. Mas talvez também faça sentido assumir que o programa Lua que rodar não vá alterar o estado -- é que claro que daí cabe ao programa respeitar isso; a garantia não é tão grande quanto re-criar o estado todas as vezes. Aí vc quem decide, já que o requisito é vc quem está dando.
Se não estou enganado, os servidores que rodam através de Fork (e.g. Apache) funcionam assim: eles sempre rodam a aplicação dentro de um fork, uma vez. Portanto, quando o processo forkado termina, o estado é eliminado. Semelhante ao seu requisito, mas usando CoW (copy-on-write) do próprio OS+hardware pra fazer o equivalente que vc faria de criar o lua state toda vez. Vc também poderia fazer desta forma!
Já outros servidores, tipo o puma do Ruby (falo dele pois conheço relativamente bem), compartilham o estado entre requisições mesmo e deixam para a aplicação a resposabilidade de não mexer no estado compartilhado. Mais suscetível à bugs, porém é comum rodar sistemas em produção dessa forma.