Activities of "cangunaydin"

  • ABP Framework version: v7.4.0
  • UI Type: Angular
  • Database System: EF Core ( PostgreSQL)
  • Tiered (for MVC) or Auth Server Separated (for Angular): yes

Hello, I am using Volo.Payment module and i customized it little bit, only payment provider we use is stripe at the time. So what i want to achieve is, when the user update the plan from stripe dashboard ( modify the payment plan, changing the productid or priceid not only date) I want to be able to modify the necessary things in db. Like finding that tenant and switching to different edition. As i see from PaymentModule

it only gets the value "current_period_end" from the webhook call payload. So PaymentUpdatedEto is not holding those values. and since the method is "private protected virtual" i can not override the method so i can pass extra parameters with the event. How can i solve the problem? should i modify the PaymentRequestAppService instead?

    public override async Task<bool> HandleWebhookAsync(string paymentGateway, string payload, Dictionary<string, string> headers)
    {
        if (paymentGateway=="stripe")
        {
            //call my service and return
        }
        await PaymentGatewayResolver.Resolve(paymentGateway).HandleWebhookAsync(payload, headers);
        return true;
    }

for the next version, is it possible to change private protected methods like "HandleCustomerSubscriptionUpdatedAsync" and "HandleCustomerSubscriptionDeletedAsync" to protected only so it could be overridden.

ok it is clear now, thank you for your time i will implement your solution i guess. Or switch to ef core native. But thanks for the help.

I see. But interesting thing is, when i save through ZEntityFramework BulkInsert method, it triggers the event for previous record. that is already in db. Why it behaves like that?

So publishing event manually is not gonna work since the same event will be triggered twice one for previous record and one for new record. Instead it should be triggered once only for the new record.

It seems like, i shouldn't use ZEntityFramework if Aggregate Root have a local event that should have been triggered afterwards. Is there any alternatives that i can use for bulk operations that will trigger the event.

Bulk operations for abp framework (https://docs.abp.io/en/abp/7.4/Repositories#bulk-operations). Is it adjusted for performance? or it is just an extension for not to do the loop?

Thanks a lot closing the ticket for now, if i find sth i can create a new ticket. Thanks for the answers.

Hello, I have sent you the email. As a note, I have created two buttons on host dashboard page. One for creating a record with shared db, and the other one is with separate db. You can use them if you want.

To reproduce the issue

  1. Create a record with shared db (By clicking the first button)
  2. Then move that record from shared db to separate db (By clicking the second button)

I have also created a separate module in the project. So it could resemble what i want to achieve in my real project. Thank you for the help.

Hello, thanks for the answers. I don't know about the 80-20 rule. You mean pareto principle? I already use the caching, my problem is about finding the limit of how much devices i can serve. If i can find it then i can plan it accordingly, by using clusters or with other solutions. In abp system, I have trouble understanding the cause, there are bunch of things going on the background for an http request which comes from middleware, by logging or opening a transactional unitofwork writing audit logs to database and etc. That's fine and i am not complaining about it. But it is difficult to read the errors as i show it on my past answer.

For ex i can not understand why abp is trying to get the Volo.Abp.LanguageManagement.Texts,k:Adzup:AbpExceptionHandling_en text from redis cache? redis-command = HMGET c:Volo.Abp.LanguageManagement.Texts,k:Adzup:AbpExceptionHandling_en

is this coming from after another exception or it is because it can not reach the redis server since it is bloated? Since this is kind of abstract discussion, I think i will move on and see with my new code how many problems i am gonna get.

I really appreciate the answers though, thank you.

and as a note, while i am creating a separate db from ui, i find out another thing. maybe this should be another ticket but i want to mention over here. look at the image below.

when you create new tenant if you are not using shared db and want to specify connection string for the module, it throws an exception as you can see in the image. Model validation is expecting extra parameters. This comes from the SaasModule.

If you create with shared db and edit the connection strings after the creation, it works.

Hello, I tried to create a sample app, at first as you say it works fine. here is the appservice that i have used.

 public async Task CreateForSeparateDb(Guid bookId, Guid tenantId)
 {
     var book = await _bookRepository.GetAsync(bookId);
     var tenantBooks=new List<Book>();
     using (CurrentTenant.Change(tenantId))
     {
         var tenantBook = new Book(GuidGenerator.Create(), book.Name + " Tenant", tenantId); //adding local event in book aggregate root.
         tenantBooks.Add(tenantBook);

         await _bookRepository.BulkInsertAsync(tenantBooks);
     }
     await _bookRepository.DeleteAsync(book);
     await _localEventBus.PublishAsync(new BookDeletedEto()
     {
         Id = book.Id,
         TenantId = null
     });

 }

Then when i go through my code, i realized that I am doing bulkinsert. I use ZEntityFramework Extensions nuget package. I named my method same as default abp repositories method (InsertManyAsync()) so i couldn't see it at first sight. Here is the code for my repository method.

        public async Task BulkInsertAsync(List<Book> books,CancellationToken cancellationToken = default)
        {
            DbContext dbContext = (DbContext)(await GetDbContextAsync());
            books.ForEach(o => { o.CreationTime = _clock.Now; });
            await dbContext.BulkInsertAsync(books,
                cancellationToken: GetCancellationToken(cancellationToken),
                options:
                (operation) => operation.BatchSize = 1000);
        }

so dbcontext is coming right

The behavior is kind of confusing here. when i bulk insert with current tenant equals null, local events are triggered. When I change the current tenant to separate db tenant, it doesn't trigger for the entity that i create but instead it triggers for the entity that is already in host db with tenantid null.

after insert what i got in event handler.

I couldn't understand from where abp is trying to find the local events. When i look at the code it seems like it is trying to get the changed entities, and getting the local events from that entity.

maybe ZEntityFramework Extensions is not marking the entities as modified but if it is like that why it works on currentTenant=null scenario?

I can send you the sample app that i have created if you want.

  • ABP Framework version: v7.4.0
  • UI Type: Angular
  • Database System: EF Core ( PostgreSQL)
  • Tiered (for MVC) or Auth Server Separated (for Angular): yes

Hello I want to serve different databases for different tenants and I know abp supports that and have a very good documentation about it. In my case i have a little bit different scenario.

I am building a server that multiple iot devices will hit the server and they are gonna be registered to host database (they do not belong to any tenant at that moment). Then the host tenant administrator needs to assign the devices to one of the tenants in the system from user interface.

So when it is assigned, i need to check if tenant has a separate db connection and according to that i will change the ICurrentTenant and insert a new data to tenant db. Then delete the data from host db. That is my plan. Sth like this.

    public async Task ApproveForSeparateDbAsync(ApproveScreenForSeparateDbDto input)
    {
        var newScreens = new List<Screen>();
        var screens = await _screenRepository.GetListByIdsAsync(input.ScreenIds);
        using (CurrentTenant.Change(input.TenantId))
        {
            foreach (var screen in screens)
            {
                var newScreen = new Screen(GuidGenerator.Create(),
                    screen.Name,
                    screen.MacAddress,
                    screen.DeviceType,
                    screen.DeviceTypeVersion,
                    screen.ModelNumber,
                    screen.ApplicationVersion,
                    screen.IpAddress,
                    screen.Port,
                    screen.CurrentClock,
                    input.TenantId);
                newScreen.Approve(input.TenantId,isSeparateDb:true);
                newScreens.Add(newScreen);
             
            }
            await _screenRepository.InsertManyAsync(newScreens);
            
        }
        foreach (var screen in screens)
        {
            await _screenManager.DeleteWithEventAsync(screen);
        }
    }

Screen is a FullAuditedAggregateRoot with IMultiTenant interface.

here is screen constructor.

 public Screen(
        Guid id,
        string name,
        string macAddress,
        string deviceType,
        int deviceTypeVersion,
        string modelNumber,
        string applicationVersion,
        string ipAddress,
        int port,
        DateTime currentClock,
        Guid? tenantId = null) : base(id)
    {
        Check.NotNullOrWhiteSpace(macAddress, nameof(macAddress));
        Check.NotNullOrWhiteSpace(deviceType, nameof(deviceType));
        Check.NotNullOrWhiteSpace(modelNumber, nameof(modelNumber));
        Check.Positive(deviceTypeVersion, nameof(deviceTypeVersion));

        TenantId = tenantId;
        UpdateName(name);
        MacAddress = macAddress;
        DeviceType = deviceType;
        DeviceTypeVersion = deviceTypeVersion;
        ModelNumber = modelNumber;
        UpdateApplicationVersion(applicationVersion);
        CurrentClock = currentClock;
        IpAddress = ipAddress;
        Port = port;
        var openingHours = OpeningHoursHelper.GetDefault().Select(o => new OpeningHoursEto()
        {
            Id = o.Id,
            Day = o.Day,
            StartTime = o.StartTime,
            EndTime = o.EndTime
        }).ToList();
        AddLocalEvent(new ScreenCreatedEto()
        {
            Id = id,
            Name = name,
            MacAddress = macAddress,
            TenantId = TenantId,
            OpeningHours = openingHours
        });
    }

you can see that over here i am trying to trigger an event so listeners can do the necessary work. when unitofwork completed it doesn't trigger any event. Weird part is if i do the same by injecting ILocalEventBus to appservice. It triggers the event. and you can see the local events on CurrentUnitOfWork.LocalEventHandlers.

Also if i change

using (CurrentTenant.Change(input.TenantId))

to

using (CurrentTenant.Change(null))

it also triggers the events. I suppose this is some kind of bug or sth i don't know when the current tenant has different Database.

Hello, Thanks for the answer.

public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => { options.ContentContributors.RemoveAll(x => x == typeof(DatabaseTemplateContentContributor)); }); }

I only want to remove this option when I use the specific templates, for the other templates like emailing, i think i need this. I don't think there is an option for that right now?

To overcome the problem for now i am adding prefix to the name of the templates that I want to exclude, and added another template content contributor

public class MyVirtualFileTemplateContentContributor : VirtualFileTemplateContentContributor, ITransientDependency
{
    private readonly ILocalizedTemplateContentReaderFactory _localizedTemplateContentReaderFactory;

    public MyVirtualFileTemplateContentContributor(
        ILocalizedTemplateContentReaderFactory localizedTemplateContentReaderFactory) : base(
        localizedTemplateContentReaderFactory)
    {
        _localizedTemplateContentReaderFactory = localizedTemplateContentReaderFactory;
    }

    public override async Task<string> GetOrNullAsync(TemplateContentContributorContext context)
    {
        if (!context.TemplateDefinition.Name.Contains(MyTemplateDefinitionProvider.TemplateGroup))
        {
            return null;
        }

        var localizedReader = await _localizedTemplateContentReaderFactory
            .CreateAsync(context.TemplateDefinition);

        return localizedReader.GetContentOrNull(
            null
        );
    }
}

so i fixed the problem in that way.

For the second question, I am not so sure about if it is a redis problem. I do not understand why redis should have a bottleneck with 2000 requests. Also I am not sure why the redis trying to get the value of AbpExceptionHandling_en from Volo.Abp.LanguageManagement.Texts

redis-command = HMGET c:Volo.Abp.LanguageManagement.Texts,k:Adzup:AbpExceptionHandling_en

With DatabaseTemplateContentContributor, the code will go through and tries to get the value with localization from the repository, so every request that hits the backend will try to go to db first

here you can see that the code will check the db and since it is a static store it will return null. And this has been tried with localization "en" and couldn't find it. Next try will be with null, and in that case the same thing will happen from dbcontributor, if you have en-En culture it will do 3 times

so it will look twice to database at least. In this process i was thinking if every request is creating 2 different connection to db it takes longer time to get the result or maybe sth else.Then it is triggering the redis overload cause it seems like when exception is happening Abp is trying to find some localization parameters from redis cache.

At the end of this story, I fixed my problem by not going to db or redis at all, with implementing ITemplateContributor. Still not sure how much clients can my app serve if there is a need to go to db for crud operations or just for querying purpose. Should i create redis cluster? should i do postgres sharding or just do load balancing? I am so confused.

Is there any benchmark that has been done with abp template for stress testing? I don't want to deploy the app and having surprises.

Since it is gonna be iot devices that will connect to server it can increase the load very much. I need to find the sweet spot for 1 instance so i can do the decisions for sharding and clusters. I am not expecting more than 10 000 devices inside the system. So i was expecting to handle that much load with 1 instance.

Showing 61 to 70 of 111 entries
Made with ❤️ on ABP v9.0.0-preview Updated on September 20, 2024, 05:21