Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sqFIA-0088kr-5B for pgsql-hackers@arkaria.postgresql.org; Mon, 16 Sep 2024 17:13:39 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1sqFI9-00Gbwq-Fp for pgsql-hackers@arkaria.postgresql.org; Mon, 16 Sep 2024 17:13:37 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sqFI9-00Gbwi-1r for pgsql-hackers@lists.postgresql.org; Mon, 16 Sep 2024 17:13:37 +0000 Received: from mail-lf1-x12a.google.com ([2a00:1450:4864:20::12a]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1sqFI1-001UNi-NL for pgsql-hackers@postgresql.org; Mon, 16 Sep 2024 17:13:35 +0000 Received: by mail-lf1-x12a.google.com with SMTP id 2adb3069b0e04-5365c060f47so5128904e87.2 for ; Mon, 16 Sep 2024 10:13:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726506808; x=1727111608; darn=postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=SoYfin6bA+WwqRWHXg7QdCMoqavbeXE4YQrtZRg3vNU=; b=g3ZaVBnFZ9OAqnmHiAlYmrnhlZPBf14GZxJbC+KLi7SFGPI9BoepLZ/f/nHv1E3dDO zyZCS06rgUungPFEm8Dhxp2XH1aTV6LyfJ2D+VlrglgwtcqLNBiQBs6rMmopEMnJbCvc BOHh4JwhV0PE7hMgrXrxr0uSngvgxsUXEZHb8FQ0OaZQidBqcNNQgtXGyEFNcrmBtagd oUS8QZTaUkMWHW1JG7QjgH69aqliOmaYTYQHVChLB1A/ALHutS0b919vPKCgq1ZxbEXc NnjylJQWN9B0pFmBDG0edAJniJmp7jvbv4cZHRPQ38lQ9tKaN5mFUE9MNrLYqPShFcLx PmIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726506808; x=1727111608; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SoYfin6bA+WwqRWHXg7QdCMoqavbeXE4YQrtZRg3vNU=; b=iDL5QoZLK5tPxhN91u+spOrByc+yVjkGejLF610u/NXzNBpft+FBKQO7BCfmFOV/+h f0oDjxhd1O+zj0IZXORBwpmUrwvKY0U8oukGkCqHDDxZHTnFNTiSlNxC9YEH6HPvQdLg MYyzXEhQtXd4dE2g00JHAhlWADErNLOsBTj3TOrtdkJUVLnecULP+7zAPxl0gb/heatp s3PqQNWpeKgliwOgWVh1n7ni5ChcezKg0aZXB5qb3oZWDJ7wpl48uBSHzKWrvM2+qBAu ZNVTXs7dxwvLqwNVdZJpXQoESA7HfOwrgnaJz7QFC4+JaBW572705yag82btCs7q31Hc ddLQ== X-Gm-Message-State: AOJu0YyKP+xI7JvRbbJsfIo7nLkdTcieWNfdq6u8NFG5vnw1auWAlbYi Bg27w9VU3DbRqlER0rL7dJ0K7n4V/6GPVAxaUwTNv80VASLlWFnCu6pPYjXIFWx2w0JfRoviIxK YXnrIrHQsROJL3PeTxa/mmj68zSdW8ZuI X-Google-Smtp-Source: AGHT+IEUflLlDKGALXjY6Fg+YcAvH/2NHLpX7jntmiqGEqw2Dzl8KziWpPr7ds6SkQFcconw5DAFHRXBzIvMk2e7xNY= X-Received: by 2002:a05:6512:1196:b0:536:55ae:7458 with SMTP id 2adb3069b0e04-53678fed155mr9429497e87.40.1726506807729; Mon, 16 Sep 2024 10:13:27 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Masahiko Sawada Date: Mon, 16 Sep 2024 10:12:50 -0700 Message-ID: Subject: Re: Using per-transaction memory contexts for storing decoded tuples To: Amit Kapila Cc: PostgreSQL-development Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Fri, Sep 13, 2024 at 3:58=E2=80=AFAM Amit Kapila wrote: > > On Thu, Sep 12, 2024 at 4:03=E2=80=AFAM Masahiko Sawada wrote: > > > > We have several reports that logical decoding uses memory much more > > than logical_decoding_work_mem[1][2][3]. For instance in one of the > > reports[1], even though users set logical_decoding_work_mem to > > '256MB', a walsender process was killed by OOM because of using more > > than 4GB memory. > > > > As per the discussion in these threads so far, what happened was that > > there was huge memory fragmentation in rb->tup_context. > > rb->tup_context uses GenerationContext with 8MB memory blocks. We > > cannot free memory blocks until all memory chunks in the block are > > freed. If there is a long-running transaction making changes, its > > changes could be spread across many memory blocks and we end up not > > being able to free memory blocks unless the long-running transaction > > is evicted or completed. Since we don't account fragmentation, block > > header size, nor chunk header size into per-transaction memory usage > > (i.e. txn->size), rb->size could be less than > > logical_decoding_work_mem but the actual allocated memory in the > > context is much higher than logical_decoding_work_mem. > > > > It is not clear to me how the fragmentation happens. Is it because of > some interleaving transactions which are even ended but the memory > corresponding to them is not released? In a generation context, we can free a memory block only when all memory chunks there are freed. Therefore, individual tuple buffers are already pfree()'ed but the underlying memory blocks are not freed. > Can we try reducing the size of > 8MB memory blocks? The comment atop allocation says: "XXX the > allocation sizes used below pre-date generation context's block > growing code. These values should likely be benchmarked and set to > more suitable values.", so do we need some tuning here? Reducing the size of the 8MB memory block would be one solution and could be better as it could be back-patchable. It would mitigate the problem but would not resolve it. I agree to try reducing it and do some benchmark tests. If it reasonably makes the problem less likely to happen, it would be a good solution. > > > We can reproduce this issue with the attached patch > > rb_excessive_memory_reproducer.patch (not intended to commit) that > > adds a memory usage reporting and includes the test. After running the > > tap test contrib/test_decoding/t/002_rb_memory.pl, we can see a memory > > usage report in the server logs like follows: > > > > LOG: reorderbuffer memory: logical_decoding_work_mem=3D268435456 > > rb->size=3D17816832 rb->tup_context=3D1082130304 rb->context=3D10862672= 64 > > > > Which means that the logical decoding allocated 1GB memory in spite of > > logical_decoding_work_mem being 256MB. > > > > One idea to deal with this problem is that we use > > MemoryContextMemAllocated() as the reorderbuffer's memory usage > > instead of txn->total_size. That is, we evict transactions until the > > value returned by MemoryContextMemAllocated() gets lower than > > logical_decoding_work_mem. However, it could end up evicting a lot of > > (possibly all) transactions because the transaction whose decoded > > tuples data are spread across memory context blocks could be evicted > > after all other larger transactions are evicted (note that we evict > > transactions from largest to smallest). Therefore, if we want to do > > that, we would need to change the eviction algorithm to for example > > LSN order instead of transaction size order. Furthermore, > > reorderbuffer's changes that are counted in txn->size (and therefore > > rb->size too) are stored in different memory contexts depending on the > > kinds. For example, decoded tuples are stored in rb->context, > > ReorderBufferChange are stored in rb->change_context, and snapshot > > data are stored in builder->context. So we would need to sort out > > which data is stored into which memory context and which memory > > context should be accounted for in the reorderbuffer's memory usage. > > Which could be a large amount of work. > > > > Another idea is to have memory context for storing decoded tuples per > > transaction. That way, we can ensure that all memory blocks of the > > context are freed when the transaction is evicted or completed. I > > think that this idea would be simpler and worth considering, so I > > attached the PoC patch, use_tup_context_per_ctx_v1.patch. Since the > > decoded tuple data is created individually when the corresponding WAL > > record is decoded but is released collectively when it is released > > (e.g., transaction eviction), the bump memory context would fit the > > best this case. One exception is that we immediately free the decoded > > tuple data if the transaction is already aborted. However, in this > > case, I think it is possible to skip the WAL decoding in the first > > place. > > > > One thing we need to consider is that the number of transaction > > entries and memory contexts in the reorderbuffer could be very high > > since it has entries for both top-level transaction entries and > > sub-transactions. To deal with that, the patch changes that decoded > > tuples of a subtransaction are stored into its top-level transaction's > > tuple context. > > > > Won't that impact the calculation for ReorderBufferLargestTXN() which > can select either subtransaction or top-level xact? Yeah, I missed that we could evict only sub-transactions when choosing the largest transaction. We need to find a better solution. > > > We always evict sub-transactions and its top-level > > transaction at the same time, I think it would not be a problem. > > Checking performance degradation due to using many memory contexts > > would have to be done. > > > > The generation context has been introduced in commit a4ccc1ce which > claims that it has significantly reduced logical decoding's memory > usage and improved its performance. Won't changing it requires us to > validate all the cases which led us to use generation context in the > first place? Agreed. Will investigate the thread and check the cases. Regards, --=20 Masahiko Sawada Amazon Web Services: https://aws.amazon.com