Giter Site home page Giter Site logo

Comments (8)

AntyaDev avatar AntyaDev commented on July 22, 2024

Hi @RomanPoprava ,
The ClientPool suppose to be used for a Closed System model.
Do you have examples of such client pools in other load-testing frameworks? If yes, could you please share the link? It will help us to re-design it properly.

from nbomber.

RomanPoprava avatar RomanPoprava commented on July 22, 2024

Unfortunately I don't have any examples in existing load-testing frameworks.
But I am going to explain what I did to gain dynamically extending client pool.
So, basically my idea was to store used and unused clients separately. I have two thread-safe lists with used and unused clients regardless.

  1. Before scenario starts - I am initiating client pool for 10 clients.
  2. Each copy of the scenario is going to take ANY available client from unused clients list. Each copy of scenario has its own thread number. I am keeping that thread number. Client is going to be moved from unused list to used list.
  3. When scenario finishes, I am going to release client by that thread number and client moves from used to unused.
  4. If unused clients list is going to run out of the clients, then I am calculating next thread number and generating new client by
    adding it used list with that thread id.

This approach can be used for testing both Open and Closed systems.

And here is some example part of the implementation:

public ScenarioProps GenerateScenario()
{
    var pool = new CustomClientPool();

    return Scenario.Create("some name", async context =>
    {
        var client = pool.GetAvailableClient();

        //step 1
        //...
        //step N
       
        pool.ReleaseClient(client.Id); // client.Id is a threadNumber
    })
        .WithInit(context => pool.Init(clientCount: 100))
        .WithClean(context => pool.Clean());
}

And the client pool class is:

 public class CustomClientPool
 {
     #region Fields

     private readonly object _lock = new();

     #endregion

     #region Properties

     protected int GeneralClientsCount { get; set; }
     protected ConcurrentDictionary<int, MyClient> UsedClients { get; } = new();
     protected ConcurrentDictionary<int, MyClient> UnusedClients { get; } = new();

     #endregion

     #region Methods

     protected MyClient GenerateClient(int id)
     {
        //create your own client
        //in my case I used my own class for Client based on FlurlClient
     }

     public override void ReleaseClient(int threadNumber)
     {
         if (UsedClients.TryRemove(threadNumber, out var client))
         {
             //Clear client headers and cookies if needed

             UnusedClients.TryAdd(threadNumber, client);
         }
     }

     private int GetNextThreadNumber()
     {
         lock (_lock)
         {
             return GeneralClientsCount++;
         }
     }

     public void Dispose()
     {
        //dispose all elements within UsedClients
        //dispose all elements within UnusedClients
     }

     public void Init(int clientsCount)
     {
         GeneralClientsCount = clientsCount;

         for (int i = 0; i < clientsCount; i++)
         {
             UnusedClients.TryAdd(i, GenerateClient(i));
         }
     }

     public MyClient GetAvailableClient()
     {
         foreach (var kvp in UnusedClients)
         {
             if (UnusedClients.TryRemove(kvp.Key, out var client))
             {
                 UsedClients.TryAdd(kvp.Key, client);

                 return client;
             }
         }

         var newThreadNumber = GetNextThreadNumber();
         var newClient = GenerateClient(newThreadNumber);
         UsedClients.TryAdd(newThreadNumber, newClient);

         return newClient;
     }

     #endregion
 }

And my client is:

public class CookiesClient
{
    public IFlurlClient FlurlClient { get; }
    public int Id { get; }
}

I am actively testing and finishing implementation of this pool right now, so please let me know what do You think, maybe You have some better ideas how to handle this case. Thank you. :)

from nbomber.

AntyaDev avatar AntyaDev commented on July 22, 2024

@RomanPoprava It seems that you would like to have a dynamically Resizable Client Pool. I like your idea, and I remember I also wanted to provide something similar. The only thing that stopped me from building it was that such an abstraction is problematic to make generic. Imagine you have a WebSocket, and you would like to use a Resizable Client Pool. Imagine now that your ClientPool is full, and it needs to be resized to add more WebSocket clients. The thing is that adding WebSocket means not just creating a WebSocketClient (like you have with HTTP) but rather creating an instance of WebSocket plus initiating a connection. Only after initializing/opening a connection can you treat such an instance of WebSocket as ready to use from your ClientPool.

Now, you have a problem: you don't want to block your test execution while waiting to initialize a new WebSocketClient. Because of this, this "resize" should be quite smart and invoked in the background before the real need to get WebScoketClient from ClientPool. Basically, we should support some configuration for the Client Pool to start a "heavy" resize process ahead of time.

I like the idea of a resizable Client Pool; we just need to define a simple API for it.

from nbomber.

RomanPoprava avatar RomanPoprava commented on July 22, 2024

As an option to implement this - create a buffer of clients for the client pool.
Let say we initialized 50 client. If the number of clients in the pool drops below the threshold (e.g. 10% of initial client pool capacity - 5 clients), then don't wait until all clients are used up, but instead, start increasing the pool by adding new 10% of clients.
Add maybe is good option will be to do this 'clients adding' in a separate thread to no block the whole execution.

from nbomber.

AntyaDev avatar AntyaDev commented on July 22, 2024

Sounds reasonable to me. Do you think you can implement it?

from nbomber.

AntyaDev avatar AntyaDev commented on July 22, 2024

ping @RomanPoprava

from nbomber.

RomanPoprava avatar RomanPoprava commented on July 22, 2024

sorry about delay, missed that message
I don't have any background with the F# before. :(

from nbomber.

AntyaDev avatar AntyaDev commented on July 22, 2024

@RomanPoprava, we can have it in C# as a Nuget package if you wish

from nbomber.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.