[JF-gofer:10028] [Draft] Linux-3.6 filesystems/xfs-delayed-logging-design

Seiji Kaneko skaneko @ a2.mbn.or.jp
2013年 2月 6日 (水) 22:40:12 JST


かねこです。題記のドラフトです。よろしくお願いします。

# かなり難しいです。
---------- >8 ---------- >8
#XFS Delayed Logging Design
#--------------------------
XFS 遅延ロギングデザイン
------------------------

#Introduction to Re-logging in XFS
#---------------------------------
XFS における再ロギング機構の紹介
--------------------------------

#XFS logging is a combination of logical and physical logging. Some objects,
#such as inodes and dquots, are logged in logical format where the details
#logged are made up of the changes to in-core structures rather than on-disk
#structures. Other objects - typically buffers - have their physical changes
#logged. The reason for these differences is to reduce the amount of log space
#required for objects that are frequently logged. Some parts of inodes are more
#frequently logged than others, and inodes are typically more frequently logged
#than any other object (except maybe the superblock buffer) so keeping the
#amount of metadata logged low is of prime importance.
XFS でのロギングは、論理ロギングと物理ロギングとの組み合わせです。inode や dquot
 などの一部のオブジェクトに対しては論理フォーマットでのロギング、すなわちログ
が取られる詳細情報とはディスク上の構造の変更ではなく、メモリ上の構造の変更にな
ります。他のオブジェクト、典型的にはバッファ、では物理的な変更のログが取られま
す。このような違いがあるのは、頻繁にログが取られるオブジェクトで必要になるログ
容量を抑えたいためです。一部の inode は他の inode より頻繁にログが取られますし、
inode は他のオブジェクトより (恐らくスーパブロックバッファを除いては) 一般的には
頻繁にログが取られますので、メタデータのログの量を抑えることはとても重要になり
ます。

#The reason that this is such a concern is that XFS allows multiple separate
#modifications to a single object to be carried in the log at any given time.
#This allows the log to avoid needing to flush each change to disk before
#recording a new change to the object. XFS does this via a method called
#"re-logging". Conceptually, this is quite simple - all it requires is that any
#new change to the object is recorded with a *new copy* of all the existing
#changes in the new transaction that is written to the log.
これがこのように関心事項となっている理由は、XFS では一つのオブジェクトに対する
複数の別々の変更を任意の時点でログに吐くことを許しているからです。これにより、
オブジェクトに対する新しい変更を記録する前の変更内容のログをディスクに書きだし
を回避可能になります。XFS はこの機能を 「再ロギング」と呼ばれる手法で実現して
います。概念的には、この手法はとても簡単です。オブジェクトに対する任意の新しい
変更を、ログに書き込まれる新しいトランザクションにすでに実行済みの変更の「新し
いコピー」と一緒に記録することを要求しているだけです。

#That is, if we have a sequence of changes A through to F, and the object was
#written to disk after change D, we would see in the log the following series
#of transactions, their contents and the log sequence number (LSN) of the
#transaction:
例で示しましょう。A から F までの一連の変更があったとし、オブジェクトは変更 D
の後でディスクに書き込まれるものとします。この場合、ログには以下の一連のトラン
ザクション (トランザクションの変更内容とログシーケンス番号 (LSN)) が書き込まれ
るでしょう。

#	Transaction		Contents	LSN
#	   A			   A		   X
#	   B			  A+B		  X+n
#	   C			 A+B+C		 X+n+m
#	   D			A+B+C+D		X+n+m+o
#	    <object written to disk>
#	   E			   E		   Y (> X+n+m+o)
#	   F			  E+F		  Y?+p
	トランザクション	変更内容	LSN
	   A			   A		   X
	   B			  A+B		  X+n
	   C			 A+B+C		 X+n+m
	   D			A+B+C+D		X+n+m+o
	    <オブジェクトがディスクに書き込まれる>
	   E			   E		   Y (> X+n+m+o)
	   F			  E+F		  Y?+p

#In other words, each time an object is relogged, the new transaction contains
#the aggregation of all the previous changes currently held only in the log.
つまり、オブジェクトが再ロギングされるたびに、現時点でログにのみ保持されている
変更に関する情報の集積が新しいトランザクションに含まれる、ということです。

#This relogging technique also allows objects to be moved forward in the log
so
#that an object being relogged does not prevent the tail of the log from ever
#moving forward.  This can be seen in the table above by the changing
#(increasing) LSN of each subsequent transaction - the LSN is effectively a
#direct encoding of the location in the log of the transaction.
この再ロギング手法は、オブジェクトの再ロギング処理の完了を待たずにログ終端への
ログの追加を行えるように、オブジェクトをログ内で前方に移動することを可能としま
す。これは上記の図で、一連のトランザクションで (増加している) LSN を見ればわか
るでしょう。LSN は等価的にログ内のトランザクションの位置を示す表現になっていま
す。
<!-- 原文があまりにも日本語にならないので展開したが、これでいいのか? -->

#This relogging is also used to implement long-running, multiple-commit
#transactions.  These transaction are known as rolling transactions, and require
#a special log reservation known as a permanent transaction reservation. A
#typical example of a rolling transaction is the removal of extents from an
#inode which can only be done at a rate of two extents per transaction because
#of reservation size limitations. Hence a rolling extent removal transaction
#keeps relogging the inode and btree buffers as they get modified in each
#removal operation. This keeps them moving forward in the log as the operation
#progresses, ensuring that current operation never gets blocked by itself if the
#log wraps around.
この再ロギングは、実行に長時間を要する複数のコミットを含むトランザクションを実
装するのにも用いられています。このようなトランザクションはローリングトランザク
ションと呼ばれ、永続的トランザクション予約として知られる特別のログ予約を必要と
します。ローリングトランザクションの典型的な例として、inode からのエクステント
の削除が挙げられます。このような削除は予約サイズの制限のため各トランザクション
ごとに 2 つのエクステントのペースでしか行えません。この結果、この削除のローリ
ングトランザクションでは inode と btree バッファは各削除トランザクションごとに
変更が入ることになり、これらの再ロギングを続けることになります。再ロギング技法
により操作の進捗と共にログが進んでいき、ログがラップアラウンドしても現在の操作
が絶対にブロックされないことが保証されています。

#Hence it can be seen that the relogging operation is fundamental to the correct
#working of the XFS journalling subsystem. From the above description, most
#people should be able to see why the XFS metadata operations writes so much to
#the log - repeated operations to the same objects write the same changes to
#the log over and over again. Worse is the fact that objects tend to get
#dirtier as they get relogged, so each subsequent transaction is writing more
#metadata into the log.
というわけで、再ロギング操作は XFS ジャーナリングサブシステムの正しい動作のた
めには基本的なものであることがわかるでしょう。上記の説明から、XFS メタデータ操
作が大量のログを吐く理由も理解していただけたと思います。同じオブジェクトに対す
る繰り返しての操作はログに同じ操作を何回も何回も書きこむからです。オブジェクト
は再ロギングが行われるとよりダーティな部分も増え、各一連のトランザクションはロ
グに更にデータを書きこむことになります。

#Another feature of the XFS transaction subsystem is that most transactions are
#asynchronous. That is, they don't commit to disk until either a log buffer is
#filled (a log buffer can hold multiple transactions) or a synchronous operation
#forces the log buffers holding the transactions to disk. This means that XFS is
#doing aggregation of transactions in memory - batching them, if you like - to
#minimise the impact of the log IO on transaction throughput.
XFS トランザクションサブシステムのもうひとつの特徴は、ほとんどのトランザクショ
ンが非同期に実行されることです。つまり、ログバッファが一杯になる (ログバッファ
には複数のトランザクションが格納されています) まで、または同期操作によりトラン
ザクションを保持しているログバッファが強制的にディスクに吐き出されるまで、トラ
ンザクションはディスクにコミットされません。つまり XFS がトランザクションのメ
モリでの統合 (そう言いたければバッチ処理) を行い、ログ IO のトランザクションス
ループットへの影響を最小限に抑えているのです。

#The limitation on asynchronous transaction throughput is the number and size of
#log buffers made available by the log manager. By default there are 8 log
#buffers available and the size of each is 32kB - the size can be increased up
#to 256kB by use of a mount option.
非同期のトランザクションスループットの制限は、ログマネージャに与えられたログバッ
ファの数と容量に依存します。標準値は、ログバッファは 8 個、各ログバッファのサ
イズは 32KB で、マウントオプションで 256KB まで増やすことが可能です。

#Effectively, this gives us the maximum bound of outstanding metadata changes
#that can be made to the filesystem at any point in time - if all the log
#buffers are full and under IO, then no more transactions can be committed until
#the current batch completes. It is now common for a single current CPU core to
#be to able to issue enough transactions to keep the log buffers full and under
#IO permanently. Hence the XFS journalling subsystem can be considered to be IO
#bound.
等価的には、この制限はファイルシステムで一度に扱うことができる未コミットのメタ
データの変更量の上限を決めています。IO 中に全てのログバッファがいっぱいとなっ
ているならば、現在のバッチが完了するまでトランザクションはそれ以上投入できませ
ん。最近の CPU のコアでは一つでも普通にログバッファを一杯にし続けるに十分の IO
 を出し続けることができるため、XFS ジャーナリングサブシステムは IO バウンドで
あると考えられています。

#Delayed Logging: Concepts
#-------------------------
遅延ロギング: その概念
----------------------

#The key thing to note about the asynchronous logging combined with the
#relogging technique XFS uses is that we can be relogging changed objects
#multiple times before they are committed to disk in the log buffers. If we
#return to the previous relogging example, it is entirely possible that
#transactions A through D are committed to disk in the same log buffer.
ここでの勘所は、XFS で用いている再ロギングと組み合わせた非同期ロギング技術では、
変更されたオブジェクトをディスクにコミットされる前にはログバッファ内に複数回再
ロギングできる、という点です。前の再ロギングの例に戻ると、トランザクション A
から D までを同じログバッファでディスクにコミットすることに何の問題もありませ
ん。

#That is, a single log buffer may contain multiple copies of the same object,
#but only one of those copies needs to be there - the last one "D", as it
#contains all the changes from the previous changes. In other words, we have one
#necessary copy in the log buffer, and three stale copies that are simply
#wasting space. When we are doing repeated operations on the same set of
#objects, these "stale objects" can be over 90% of the space used in the log
#buffers. It is clear that reducing the number of stale objects written to the
#log would greatly reduce the amount of metadata we write to the log, and this
#is the fundamental goal of delayed logging.
つまり、一つのログバッファに同じオブジェクトの写しを複数回含めることができます
が、これらの写しの中で必要なのは一つだけ、最後の "D" のみで、これに以前の変更
が全て含まれています。言い換えれば、ログバッファの必要なコピーは一つだけで、他
の不要な写しは容量を食いつぶしているだけです。同じオブジェクトに対する動作を繰
り返し行なっている場合には、この無駄なオブジェクトがログバッファの 90% 以上を
消費してしまうこともあります。したがってログに書きこむ不要なオブジェクトの数を
減らすことがログに書きこむメタデータの量を大幅に減らします。これが遅延ロギング
の基本的な狙いです。

#From a conceptual point of view, XFS is already doing relogging in memory (where
#memory == log buffer), only it is doing it extremely inefficiently. It is using
#logical to physical formatting to do the relogging because there is no
#infrastructure to keep track of logical changes in memory prior to physically
#formatting the changes in a transaction to the log buffer. Hence we cannot avoid
#accumulating stale objects in the log buffers.
概念的には、XFS ではすでにメモリ上で (メモリ = ログバッファ) ですでに再ロギン
グを行なっているが、その方法は極めて非効率である、ということになります。メモリ
には、トランザクション内の変更を物理フォーマットに変更してログバッファに書きこ
む前に論理的変更の履歴を追跡できるようなインフラストラクチャはありませんから、
再ロギングを行うために論理から物理フォーマットの変換を行なっています。このため、
ログバッファ上に無駄なオブジェクトが蓄積するのを避けることができないのです。

#Delayed logging is the name we've given to keeping and tracking transactional
#changes to objects in memory outside the log buffer infrastructure. Because of
#the relogging concept fundamental to the XFS journalling subsystem, this is
#actually relatively easy to do - all the changes to logged items are already
#tracked in the current infrastructure. The big problem is how to accumulate
#them and get them to the log in a consistent, recoverable manner.
#Describing the problems and how they have been solved is the focus of this
#document.
遅延ロギングは、ログバッファインフラストラクチャの外でメモリ上のオブジェクトの
トランザクションの変更履歴を管理する手法に付けられた名称です。再ロギングが XFS
 ジャーナリングサブシステムの基礎となる概念であるため、このような手法は実際は
容易に実現できます。なんとなればログに書きこまれたアイテムの変更は全て現状のイ
ンフラストラクチャで履歴管理されているからです。大きな問題は、それを一貫性のと
れた回復可能な手法で、どうやって蓄積し取得するか、にあります。この問題と解決方
法を詳細に記載することがこの文書の目的となります。

#One of the key changes that delayed logging makes to the operation of the
#journalling subsystem is that it disassociates the amount of outstanding
#metadata changes from the size and number of log buffers available. In other
#words, instead of there only being a maximum of 2MB of transaction changes not
#written to the log at any point in time, there may be a much greater amount
#being accumulated in memory. Hence the potential for loss of metadata on a
#crash is much greater than for the existing logging mechanism.
遅延ロギングがジャーナリングサブシステムの動作に行った鍵となる変更の一つは、仕
かかり中のメタデータの変更の量と、提供されているログバッファの量と数を切離した
ことです。言い換えれば、任意の時点でログに書きこまれていない変更トランザクショ
ンの量を 2MB までとする制限をおこなうのではなく、メモリにはるかに大きな量を蓄
えられるようにしています。このためクラッシュ時に失われるメタデータの量は、既存
のロギング手法に比べてずっと大きくなる可能性があります。

#It should be noted that this does not change the guarantee that log recovery
#will result in a consistent filesystem. What it does mean is that as far as the
#recovered filesystem is concerned, there may be many thousands of transactions
#that simply did not occur as a result of the crash. This makes it even more
#important that applications that care about their data use fsync() where they
#need to ensure application level data integrity is maintained.
ただし、これはログリカバリによって一貫したファイルシステムが得られるという保証
には影響しないことは特記しておきます。つまり、回復されたファイルシステムに関し
て言えば、クラッシュの結果数千トランザクションの処理が単になかったことにされる
だけです。また、アプリケーションからデータの整合性を保証するためには、書き込ん
だデータを fsync() で面倒を見ることが一層重要にもなります。

#It should be noted that delayed logging is not an innovative new concept that
#warrants rigorous proofs to determine whether it is correct or not. The method
#of accumulating changes in memory for some period before writing them to the
#log is used effectively in many filesystems including ext3 and ext4. Hence
#no time is spent in this document trying to convince the reader that the
#concept is sound. Instead it is simply considered a "solved problem" and as
#such implementing it in XFS is purely an exercise in software engineering.
また、遅延ロギングは正当性の判断を厳格な証明で保証するような斬新な新コンセプト
ではないことも述べておきます。変更をログに書きこむ前しばらくの間メモリに蓄積す
る方法は、ext3 や ext4 を含む多くのファイルシステムですでに効果的に使われてい
ます。従ってこの文書ではこの方法の正当性を読者に印象づけることはおこないまぜん。
これは単に「解決された問題」と考えられており、XFS でのこの方法の実装はソフトウェ
ア工学の純粋な実践というべきものです。

#The fundamental requirements for delayed logging in XFS are simple:
XFS での遅延ロギングの基本的要求事項は単純なものです。

#	1. Reduce the amount of metadata written to the log by at least
#	   an order of magnitude.
#	2. Supply sufficient statistics to validate Requirement #1.
#	3. Supply sufficient new tracing infrastructure to be able to debug
#	   problems with the new code.
#	4. No on-disk format change (metadata or log format).
#	5. Enable and disable with a mount option.
#	6. No performance regressions for synchronous transaction workloads.
	1. ログに書きこむメタデータの量を少なくとも一桁以上削減する。
	2. #1 の要求を満たしていることを検証するのに十分な量の統計情報を採取す
る。
	3. 新しいコードでの問題をデバッグするのに十分なトレースの枠組みを新規
に提供する。
	4. ディスク上のフォーマットの変更は行わない (メタデータおよびログフォー
マット)。
	5. マウントオプションで有効化・無効化が行えるようにする。
	6. 同期トランザクション負荷では、性能が低下しないようにする。

#Delayed Logging: Design
#-----------------------
遅延ロギングの設計
------------------

#Storing Changes
変更を格納する

#The problem with accumulating changes at a logical level (i.e. just using the
#existing log item dirty region tracking) is that when it comes to writing the
#changes to the log buffers, we need to ensure that the object we are formatting
#is not changing while we do this. This requires locking the object to prevent
#concurrent modification. Hence flushing the logical changes to the log would
#require us to lock every object, format them, and then unlock them again.
論理レベルで (即ち、単に現行のダーティな領域のトラッキングログアイテムを用いる)
 変更を蓄積する際の問題は、ログバッファに書き込む点まで来た際に、以降フォーマッ
ティングを行なっているオブジェクトが作業中に変更されないことを保証する必要があ
るという点です。このために変更が並行して進行しないようオブジェクトをロックする
必要があります。このため、ログに対して論理的変更を吐き出すには、すべてのオブジェ
クトをロックし、フォーマットし、そのあとでロックを解除する必要があります。

#This introduces lots of scope for deadlocks with transactions that are already
#running. For example, a transaction has object A locked and modified, but needs
#the delayed logging tracking lock to commit the transaction. However, the
#flushing thread has the delayed logging tracking lock already held, and is
#trying to get the lock on object A to flush it to the log buffer. This appears
#to be an unsolvable deadlock condition, and it was solving this problem that
#was the barrier to implementing delayed logging for so long.
このことは、すでに走っているトランザクション間でのデッドロックが発生する多数の
可能性があるということです。例えばトランザクションがオブジェクト A をロックし
て変更しており、トランザクションをコミットするために遅延ロギングトラッキングロッ
クを取得しようとしているとします。ところが、遅延ロギングトラッキングロックを既
に持っているフラッシュを行うスレッドが、ログバッファに内容をフラッシュするため
にオブジェクト A をロックしようとしている場合、解決不能なデッドロック状態であ
り、これが長い間にわたって遅延ロギングを実装するにあたっての障害となっていまし
た。

#The solution is relatively simple - it just took a long time to recognise it.
#Put simply, the current logging code formats the changes to each item into an
#vector array that points to the changed regions in the item. The log write code
#simply copies the memory these vectors point to into the log buffer during
#transaction commit while the item is locked in the transaction. Instead of
#using the log buffer as the destination of the formatting code, we can use an
#allocated memory buffer big enough to fit the formatted vector.
解決策は比較的単純です - それに気がつくまでに単に長い時間がかかっただけです。
単純に言えば、現在のロギングコードは、各アイテムの形式をフォーマットにより変更
し、アイテム中の変更箇所を指すベクタ配列に格納しています。ログ書きこみコードは
トランザクションコミット中には単にこれらベクタの指すメモリを単にログバッファに
コピーするだけで、その間対象のアイテムはロックされています。ログバッファをフォー
マット変換コードの目的地として使う代わりに、変換されたベクタが十分入る大きさの
メモリを用いればいいのです。

#If we then copy the vector into the memory buffer and rewrite the vector to
#point to the memory buffer rather than the object itself, we now have a copy of
#the changes in a format that is compatible with the log buffer writing code.
#that does not require us to lock the item to access. This formatting and
#rewriting can all be done while the object is locked during transaction commit,
#resulting in a vector that is transactionally consistent and can be accessed
#without needing to lock the owning item.
もしベクタをメモリバッファにコピーし、更にオブジェクト自体ではなくメモリバッファ
を指すようにベクタを書き換えてしまえば、アイテムをロックしてアクセスする必要な
くログバッファ書きこみコードと互換性のある形式の変更内容の写しを得ることができ
ます。この形式変換と再書き込みは、トランザクションコミット中でオブジェクトがロッ
クされている間に全て行うことが可能なため、トランザクションに対して等価で、アイ
テムをロックにより所有する必要がなくアクセス可能なベクタを得ることが可能です。

#Hence we avoid the need to lock items when we need to flush outstanding
#asynchronous transactions to the log. The differences between the existing
#formatting method and the delayed logging formatting can be seen in the
#diagram below.
こうやって、未処理の非同期トランザクションをログにフラッシュする必要がある際に
もアイテムのロックを回避しています。現在の形式変換処理と遅延ロギングでの形式変
換処理との違いを以下の図に示します。

#Current format log vector:
従来のログベクタの形式変換処理:

#Object    +---------------------------------------------+
#Vector 1      +----+
#Vector 2                    +----+
#Vector 3                                   +----------+
オブジェクト	+---------------------------------------------+
ベクタ 1 	    +----+
ベクタ 2	                 +----+
ベクタ 3 	                                +----------+

#After formatting:
フォーマット後では:

#Log Buffer    +-V1-+-V2-+----V3----+
ログバッファ	+-V1-+-V2-+----V3----+

#Delayed logging vector:
遅延ロギングでのベクタ:

#Object    +---------------------------------------------+
#Vector 1      +----+
#Vector 2                    +----+
#Vector 3                                   +----------+
オブジェクト	+---------------------------------------------+
ベクタ 1 	    +----+
ベクタ 2	                 +----+
ベクタ 3 	                                +----------+

#After formatting:
フォーマット後では:

#Memory Buffer +-V1-+-V2-+----V3----+
#Vector 1      +----+
#Vector 2           +----+
#Vector 3                +----------+
メモリバッファ +-V1-+-V2-+----V3----+
ベクタ 1       +----+
ベクタ 2            +----+
ベクタ 3                 +----------+

#The memory buffer and associated vector need to be passed as a single object,
#but still need to be associated with the parent object so if the object is
#relogged we can replace the current memory buffer with a new memory buffer that
#contains the latest changes.
メモリバッファと関連付けられたベクタは単一のオブジェクトとして扱われる必要があ
りますが、同時に親オブジェクトとも関連付けて扱い、もしオブジェクトが再ロギング
された場合には現在のメモリバッファを新しいメモリバッファに置き換えて最新の変更
を含むようにしてやる必要があります。

#The reason for keeping the vector around after we've formatted the memory
#buffer is to support splitting vectors across log buffer boundaries correctly.
#If we don't keep the vector around, we do not know where the region boundaries
#are in the item, so we'd need a new encapsulation method for regions in the log
#buffer writing (i.e. double encapsulation). This would be an on-disk format
#change and as such is not desirable.  It also means we'd have to write the log
#region headers in the formatting stage, which is problematic as there is per
#region state that needs to be placed into the headers during the log write.
メモリバッファの形式変換を行った後もベクタをしばらくの間保持していく理由は、ロ
グバッファのリージョン境界で分割されたベクタをサポートする為です。ベクタをしば
らく保持していなかった場合、アイテム内のリージョン境界がどこであったのかが分か
らなくなるため、二重カプセル化などの新しいカプセル化の方法がログバッファで必要
になります。これはディスク上の形式の変更を伴うため、望ましくありません。また、
形式変換の段階でログリージョンの書き込みを行う必要も出てきますが、これはログラ
イト中にリージョン毎の状態をヘッダに書きこまなければならないため、問題になりま
す。

#Hence we need to keep the vector, but by attaching the memory buffer to it and
#rewriting the vector addresses to point at the memory buffer we end up with a
#self-describing object that can be passed to the log buffer write code to be
#handled in exactly the same manner as the existing log vectors are handled.
#Hence we avoid needing a new on-disk format to handle items that have been
#relogged in memory.
このようなわけでベクタの保持は必要ではありますが、それにメモリバッファを割り当
ててベクタアドレスをメモリバッファを指すよう書き換えることによって、既存のログ
ベクタの処理と全く同じ方法で扱うことができるようログバッファ書き込みコードに渡
すことの可能な自己言及型のオブジェクトを得ることができます。これにより、メモリ
上の再ロギングで取得されたアイテムの処理のための、新しいディスク上のデータ形式
の必要性を回避できています。


#Tracking Changes
変更のトラッキング

#Now that we can record transactional changes in memory in a form that allows
#them to be used without limitations, we need to be able to track and accumulate
#them so that they can be written to the log at some later point in time.  The
#log item is the natural place to store this vector and buffer, and also makes sense
#to be the object that is used to track committed objects as it will always
#exist once the object has been included in a transaction.
ここまでで、変更トランザクションの記録をメモリ上に制限なく利用可能な形で蓄える
ことができるようになりました。次はこれらを追跡処理 (トラッキング) して蓄積し、
後の適切なタイミングでログに書きこむことができるようにしなければなりません。ロ
グアイテムはこのベクタとバッファを格納するのに自然な場所ですし、オブジェクトが
トランザクションに含まれた後は常に存在し続けるため、コミットされたオブジェクト
を追跡するのに使うオブジェクトとするのも妥当です。

#The log item is already used to track the log items that have been written to
#the log but not yet written to disk. Such log items are considered "active"
#and as such are stored in the Active Item List (AIL) which is a LSN-ordered
#double linked list. Items are inserted into this list during log buffer IO
#completion, after which they are unpinned and can be written to disk. An object
#that is in the AIL can be relogged, which causes the object to be pinned again
#and then moved forward in the AIL when the log buffer IO completes for that
#transaction.
このログアイテムは、ログには書かれているがディスクには反映されていないログアイ
テムをトラッキングするために既に使われています。このようなログアイテムを「アク
ティブ」な状態であるとし、LSN 順の双方向リンクトリストであるアクティブアイテム
リスト (AIL) に格納します。アイテムは、ログバッファ IO 完了処理時にこのリスト
に挿入され、IO 処理後にはピンどめが外されていつでもディスクに書いていい状態に
なります。AIL に入っているオブジェクトは再ロギング可能で、その場合オブジェクト
は再度ピンどめされ、対象トランザクションのログバッファ IO 完了時に AIL の前方
に移動されます。

#Essentially, this shows that an item that is in the AIL can still be modified
#and relogged, so any tracking must be separate to the AIL infrastructure. As
#such, we cannot reuse the AIL list pointers for tracking committed items, nor
#can we store state in any field that is protected by the AIL lock. Hence the
#committed item tracking needs it's own locks, lists and state fields in the
log
#item.
本質的には、このことは AIL 内のアイテムはまだ変更可能で再ロギング可能であるこ
とを示しており、各トラッキングは AIL インフラストラクチャ上で別別のものとして
扱われなければなりません。このため、AIL のリストポインタをコミットしたアイテム
のトラッキングに流用することは出来ず、AIL ロックで保護されているどのフィールド
にも状態を格納することもできません。従ってコミット済みのアイテムのトラッキング
には自前のロック、リストと状態フィールドがログアイテム内に必要です。

#Similar to the AIL, tracking of committed items is done through a new list
#called the Committed Item List (CIL).  The list tracks log items that have been
#committed and have formatted memory buffers attached to them. It tracks objects
#in transaction commit order, so when an object is relogged it is removed from
#it's place in the list and re-inserted at the tail. This is entirely arbitrary
#and done to make it easy for debugging - the last items in the list are the
#ones that are most recently modified. Ordering of the CIL is not necessary for
#transactional integrity (as discussed in the next section) so the ordering is
#done for convenience/sanity of the developers.
AIL と同様に、コミット済みのアイテムのトラッキングはコミット済みアイテムリスト
 (CIL) という別のリストを使って管理されます。このリストはコミット済みで、フォー
マット済みのメモリバッファに関連付けられたログアイテムをトラッキングします。こ
のリストはトランザクションのコミット順にオブジェクトをトラッキングしますので、
オブジェクトが再ロギングされる際にはリストの元の場所から削除され、しっぽの位置
に最挿入されます。これは実はやってもやらなくてもいい処理で、デバッグの容易化の
ためにおこなわれています。リストの最後のアイテムは最も最近に変更されたものにな
るようにしています。CIL の順序は必ずしもトランザクションとしての整合性を満たす
順序である必要はなく (このことは次の章で詳しく説明します)、開発者の都合や SAN
値で決めて構いません。


#Delayed Logging: Checkpoints
遅延ロギング: チェックポイント

#When we have a log synchronisation event, commonly known as a "log force",
#all the items in the CIL must be written into the log via the log buffers.
#We need to write these items in the order that they exist in the CIL, and they
#need to be written as an atomic transaction. The need for all the objects to be
#written as an atomic transaction comes from the requirements of relogging and
#log replay - all the changes in all the objects in a given transaction must
#either be completely replayed during log recovery, or not replayed at all. If
#a transaction is not replayed because it is not complete in the log, then
#no later transactions should be replayed, either.
ログ同期イベント (通称強制ログ書き出し (log force)) の際には、CIL 内の全アイテ
ムがログバッファ経由でログに吐き出されなければなりません。また、これらのアイテ
ムを CIL に存在する順序で書きだす必要があり、また単一 (atomic) の処理として書
き込みが行われなければなりません。全てのオブジェクトが単一トランザクションとし
て書かれなければならないという制約は、再ロギングとログリプレイの要求、すなわち
あるトランザクションの全てのオブジェクトの全ての変更は、ログリカバリの際に完全
にリプレイされるか、全くリプレイされないかのいずれかであること、に起因します。
トランザクションがログ内で完結しないためリプレイされない場合には、その後のトラ
ンザクションも同様にリプレイされません。

#To fulfill this requirement, we need to write the entire CIL in a single log
#transaction. Fortunately, the XFS log code has no fixed limit on the size of a
#transaction, nor does the log replay code. The only fundamental limit is that
#the transaction cannot be larger than just under half the size of the log.  The
#reason for this limit is that to find the head and tail of the log, there must
#be at least one complete transaction in the log at any given time. If a
#transaction is larger than half the log, then there is the possibility that a
#crash during the write of a such a transaction could partially overwrite the
#only complete previous transaction in the log. This will result in a recovery
#failure and an inconsistent filesystem and hence we must enforce the maximum
#size of a checkpoint to be slightly less than a half the log.
この要求を満たすためには、CIL 全体を単一のトランザクションとして書きだす必要が
あります。幸い、XFS ログコードにはトランザクションのサイズの決まった制限はなく、
ログリプレイコードにも制限はありません。唯一の根本的な制限は、トランザクション
はログサイズの半分以下でなければならない、という点です。この制限は、ログの開始
点と末尾を探す際に、少なくとも一つの完全なトランザクションが常にログ内に存在し
なければならないことから来ています。トランザクションがログの半分より大きい場合
には、そのようなトランザクションの書き込み途中にクラッシュが発生した場合、その
処理でログ内の唯一の完全なトランザクションの一部を上書きしてしまう可能性がある
からです。この上書きが起こってしまうと回復は失敗し、不整合なファイルシステムと
なってしまいますので、チェックポイントの最大サイズはログの半分より多少小さいも
のとしなければならないのです。

#Apart from this size requirement, a checkpoint transaction looks no different
#to any other transaction - it contains a transaction header, a series of
#formatted log items and a commit record at the tail. From a recovery
#perspective, the checkpoint transaction is also no different - just a lot
#bigger with a lot more items in it. The worst case effect of this is that we
#might need to tune the recovery transaction object hash size.
このサイズの制限以外に、チェックポイントトランザクションは他のトランザクション
と見た目の違いはありません。トランザクションヘッダ、一連のフォーマットされたロ
グアイテムが続き、最後にコミットレコードです。リカバリの見地からはチェックポイ
ントトランザクションも全く違いはありません。単にもっとたくさんのアイテムを含む
もっと大きなトランザクションなだけです。このことからの最悪の場合の効果として、
リカバリトランザクションのオブジェクトハッシュサイズの調整が必要となる場合があ
ります。

#Because the checkpoint is just another transaction and all the changes to log
#items are stored as log vectors, we can use the existing log buffer writing
#code to write the changes into the log. To do this efficiently, we need to
#minimise the time we hold the CIL locked while writing the checkpoint
#transaction. The current log write code enables us to do this easily with the
#way it separates the writing of the transaction contents (the log vectors)
#from the transaction commit record, but tracking this requires us to have a
#per-checkpoint context that travels through the log write process through to
#checkpoint completion.
チェックポイントもトランザクションであり、すべてのログアイテムへの変更はログベ
クタとして格納されるため、既存のログバッファ書きこみコードをログへの変更の書き
込みに用いることができます。これを効率良く行うためには、チェックポイントトラン
ザクションを書き込んでいる間の CIL ロックの保持期間を最小化する必要があります。
現在のログ書きこみコードでは、トランザクションコミットレコードとトランザクショ
ンの内容 (ログベクタ) の書き込みを分離することにより、これを容易に行うことが可
能になっていますが、これをトラッキングするにはチェックポイント完了までのログ書
き込み期間の間、走行中のチェックポイント毎のコンテキストを持つ必要が出てきます。

#Hence a checkpoint has a context that tracks the state of the current
#checkpoint from initiation to checkpoint completion. A new context is initiated
#at the same time a checkpoint transaction is started. That is, when we remove
#all the current items from the CIL during a checkpoint operation, we move all
#those changes into the current checkpoint context. We then initialise a new
#context and attach that to the CIL for aggregation of new transactions.
つまり、チェックポイントは、チェックポイント処理の開始時から完了時までのチェッ
クポイントの現在の状態を追跡するコンテキストをもつことになります。新しいコンテ
キストはチェックポイントトランザクションの開始と同時に初期化されます。つまり、
チェックポイント処理中に CIL から現在のアイテムを全て削除したあと、これらの変
更をすべて現在のチェックポイントコンテキストに移動することになります。その後、
新しいコンテキストを初期化し、新しいトランザクションの集約のためにこの CIL に
アタッチします。

#This allows us to unlock the CIL immediately after transfer of all the
#committed items and effectively allow new transactions to be issued while we
#are formatting the checkpoint into the log. It also allows concurrent
#checkpoints to be written into the log buffers in the case of log force heavy
#workloads, just like the existing transaction commit code does. This, however,
#requires that we strictly order the commit records in the log so that
#checkpoint sequence order is maintained during log replay.
これにより、コミット済みのアイテムの転送を全て済ませたらすぐに CIL のロック開
放が行えますし、チェックポイントをログにフォーマットする処理に並行して新しいト
ランザクションを効率的に発行可能になります。また、ログが重い負荷を招く場合には、
既存のトランザクションコミットコードが行なっている処理と同様にログバッファへ複
数のチェックポイントを並列書込することも可能となります。しかしながら、このため
にはログ内のコミットレコードの厳密な順序を維持し、チェックポイントの順番がログ
リプレイ中に維持できるようにしなければなりません。

#To ensure that we can be writing an item into a checkpoint transaction at
#the same time another transaction modifies the item and inserts the log item
#into the new CIL, then checkpoint transaction commit code cannot use log items
#to store the list of log vectors that need to be written into the transaction.
#Hence log vectors need to be able to be chained together to allow them to be
#detached from the log items. That is, when the CIL is flushed the memory
#buffer and log vector attached to each log item needs to be attached to the
#checkpoint context so that the log item can be released. In diagrammatic form,

#the CIL would look like this before the flush:
アイテムをチェックポイントトランザクションに書きこむのと同時に他のトランザクショ
ンがそのアイテムを変更して新しいログアイテムを新しい CIL に挿入できることを保
証するためには、チェックポイントトランザクションのコミットコードはトランザクショ
ンに書きこむ必要があるログベクタのリストの格納場所としてログアイテムを使うこと
はできません。このため、ログベクタはログアイテムから切り離すことができるように
チェインできる必要があります。このことから、CIL がフラッシュされた際に、メモリ
バッファと各ログアイテムにアタッチされたログベクタはログアイテムが開放できるよ
うにチェックポイントコンテキストにアタッチする必要があります。図示しますと、CIL
 はフラッシュ前には以下のようになっているでしょう。

#	CIL Head
#	   |
#	   V
#	Log Item <-> log vector 1	-> memory buffer
#	   |				-> vector array
#	   V
#	Log Item <-> log vector 2	-> memory buffer
#	   |				-> vector array
#	   V
#	......
#	   |
#	   V
#	Log Item <-> log vector N-1	-> memory buffer
#	   |				-> vector array
#	   V
#	Log Item <-> log vector N	-> memory buffer
#					-> vector array
	CIL 先頭
	   |
	   V
	ログアイテム <-> ログベクタ 1	-> メモリバッファ
	   |				-> ベクタ配列
	   V
	ログアイテム <-> ログベクタ 2	-> メモリバッファ
	   |				-> ベクタ配列
	   V
	......
	   |
	   V
	ログアイテム <-> ログベクタ N-1	-> メモリバッファ
	   |				-> ベクタ配列
	   V
	ログアイテム <-> ログベクタ N	-> メモリバッファ
					-> ベクタ配列

#And after the flush the CIL head is empty, and the checkpoint context log
#vector list would look like:
そして、フラッシュ後は CIL 先頭は空になり、チェックポイントコンテキストログベ
クタリストは以下のようになります。

#	Checkpoint Context
#	   |
#	   V
#	log vector 1	-> memory buffer
#	   |		-> vector array
#	   |		-> Log Item
#	   V
#	log vector 2	-> memory buffer
#	   |		-> vector array
#	   |		-> Log Item
#	   V
#	......
#	   |
#	   V
#	log vector N-1	-> memory buffer
#	   |		-> vector array
#	   |		-> Log Item
#	   V
#	log vector N	-> memory buffer
#			-> vector array
#			-> Log Item
	チェックポイントコンテキスト
	   |
	   V
	ログベクタ 1	-> メモリバッファ
	   |		-> ベクタ配列
	   |		-> ログアイテム
	   V
	log vector 2	-> メモリバッファ
	   |		-> ベクタ配列
	   |		-> ログアイテム
	   V
	......
	   |
	   V
	log vector N-1	-> メモリバッファ
	   |		-> ベクタ配列
	   |		-> ログアイテム
	   V
	log vector N	-> メモリバッファ
	   |		-> ベクタ配列
	   |		-> ログアイテム

#Once this transfer is done, the CIL can be unlocked and new transactions can
#start, while the checkpoint flush code works over the log vector chain to
#commit the checkpoint.
この変換が済み次第、CIL のロックは解除でき、新しいトランザクションを開始可能で
す。一方チェックポイントフラッシュコードはログベクタチェインを処理してチェック
ポイントのコミットを進めます。

#Once the checkpoint is written into the log buffers, the checkpoint context is
#attached to the log buffer that the commit record was written to along with a
#completion callback. Log IO completion will call that callback, which can then
#run transaction committed processing for the log items (i.e. insert into AIL
#and unpin) in the log vector chain and then free the log vector chain and
#checkpoint context.
チェックポイントがログバッファに書きこまれてしまえば、チェックポイントのコンテ
キストはログバッファにアタッチされており、そのログバッファにはコミットレコード
と完了コールバックも共に書き込んまれています。ログ IO 完了時にはこのコールバッ
クが呼ばれ、コールバックではログベクタチェイン内のログアイテムに対する、トラン
ザクションのコミット処理 (AIL に挿入してピン止めを外す) が実行され、その後ログ
ベクタチェインとチェックポイントのコンテキストを開放します。

#Discussion Point: I am uncertain as to whether the log item is the most
#efficient way to track vectors, even though it seems like the natural way to do
#it. The fact that we walk the log items (in the CIL) just to chain the log
#vectors and break the link between the log item and the log vector means that
#we take a cache line hit for the log item list modification, then another for
#the log vector chaining. If we track by the log vectors, then we only need to
#break the link between the log item and the log vector, which means we should
#dirty only the log item cachelines. Normally I wouldn't be concerned about one
#vs two dirty cachelines except for the fact I've seen upwards of 80,000 log
#vectors in one checkpoint transaction. I'd guess this is a "measure and
#compare" situation that can be done after a working and reviewed implementation
#is in the dev tree....
考慮点: ログアイテムがベクタを追跡するのに最も適切な手段であるかどうかははっき
りしません。ただし、一番自然な手段のようには思えます。私達はログベクタをチェイ
ンする、あるいはログアイテムとログベクタ間のリンクを切るために CIL 内のログア
イテムをたぐる処理を行なっているためログアイテムリスト変更でキャッシュラインヒッ
トとなり、またログベクタをチェインする処理でまたキャッシュラインヒットとなりま
す。ログベクタを用いて追跡を行なっている場合、ログアイテムとログベクタ間のリン
クを切るだけでよく、言い換えればログアイテムキャッシュラインのみをダーティにす
べきだ、ということになります。通常はダーティにするキャッシュラインがひとつかふ
たつかということは問題にしないのですが、一つのチェックポイントトランザクション
でログベクタが 80,000 にもなる状況を見たことがあるのです。恐らくこれは「測定し
て比較せよ」という状況とは思いますが、dev ツリーの実装に着手し、検証してからで
なくては測定ができません。

#Delayed Logging: Checkpoint Sequencing
遅延ロギング: チェックポイントの番号付け

#One of the key aspects of the XFS transaction subsystem is that it tags
#committed transactions with the log sequence number of the transaction commit.
#This allows transactions to be issued asynchronously even though there may be
#future operations that cannot be completed until that transaction is fully
#committed to the log. In the rare case that a dependent operation occurs (e.g.
#re-using a freed metadata extent for a data extent), a special, optimised log
#force can be issued to force the dependent transaction to disk immediately.
XFS トランザクションサブシステムの鍵となる面の一つは、コミットされたトランザク
ションに、トランザクションコミットのログシーケンス番号がタグ付されるという点で
す。これにより、トランザクションが完全にログにコミットされるまで完了できないよ
うな未来の処理がある場合でさえ、トランザクションを非同期に発行することが可能に
なります。稀な場合として依存処理が発生したばあいでも (つまり、解放されたメタデー
タエクステントをデータエクステントとして再利用する場合)、特別の、最適化された
ログ処理から依存するトランザクションを強制的にディスクに即時に書きこむよう指示
することが可能になります。

#To do this, transactions need to record the LSN of the commit record of the
#transaction. This LSN comes directly from the log buffer the transaction is
#written into. While this works just fine for the existing transaction
#mechanism, it does not work for delayed logging because transactions are not
#written directly into the log buffers. Hence some other method of sequencing
#transactions is required.
これを可能にするためには、トランザクションはトランザクションコミットレコードの
 LSN を記録しておく必要があります。この LSN は、トランザクションが書きこもうと
しているログバッファから直接得られます。この処理は既存のトランザクション機構で
は全く問題なく動作しますが、遅延ロギングではトランザクションは直接ログバッファ
に書かれるわけではないためうまく動きません。このため、トランザクションの番号付
けのための別の方法が必要になります。

#As discussed in the checkpoint section, delayed logging uses per-checkpoint
#contexts, and as such it is simple to assign a sequence number to each
#checkpoint. Because the switching of checkpoint contexts must be done
#atomically, it is simple to ensure that each new context has a monotonically
#increasing sequence number assigned to it without the need for an external
#atomic counter - we can just take the current context sequence number and add
#one to it for the new context.
チェックポイントの節で説明したように、遅延ロギングはチェックポイント毎のコンテ
キストを使いますので、各チェックポイントに対して連番をつけるのが簡単です。チェッ
クポイントコンテキストの切り替えはアトミックに行わなければならないため、外部の
単一のカウンタを使わずとも各々の新コンテキストが単調に増加する連番を持つことを
保証するのは簡単です。単に現在のコンテキストシーケンス番号を使い、新しいコンテ
キストには 1 足したものを使えば良いのです。

#Then, instead of assigning a log buffer LSN to the transaction commit LSN
#during the commit, we can assign the current checkpoint sequence. This allows
#operations that track transactions that have not yet completed know what
#checkpoint sequence needs to be committed before they can continue. As a
#result, the code that forces the log to a specific LSN now needs to ensure that
#the log forces to a specific checkpoint.
そして、コミットの際にログバッファ LSN をトランザクションコミット LSN として割
り当てる代わりに、現在のチェックポイントシーケンス番号を割り当てることができま
す。これにより、完了していないトランザクションの追跡処理で、後の処理を続ける前
にどのチェックポイントシーケンスをコミットする必要があるかを知ることができます。
その結果、ログを特定の LSN に強制的に吐き出すコードは、ログが特定のチェックポ
イントに強制的に吐き出されていることを保証する必要が出てきます。

#To ensure that we can do this, we need to track all the checkpoint contexts
#that are currently committing to the log. When we flush a checkpoint, the
#context gets added to a "committing" list which can be searched. When a
#checkpoint commit completes, it is removed from the committing list. Because
#the checkpoint context records the LSN of the commit record for the checkpoint,

#we can also wait on the log buffer that contains the commit record, thereby
#using the existing log force mechanisms to execute synchronous forces.
これが確実にできるようにするためには、現在ログにコミット中の全てのチェックポイ
ントコンテキストを追跡する必要があります。チェックポイントをフラッシュする際に
は、コンテキストは探索可能な「コミット中」リストに加えられます。またチェックポ
イントコミットが完了した時点で、コンテキストは「コミット中」リストから削除され
ます。チェックポイントコンテキストにはチェックポイントに対するコミットレコード
の LSN が記録されているため、コミットレコードを含むログバッファを待ちつづける
ことができ、既存のログ書き出し機構を用いて同期書き出しを行うことができます。

#It should be noted that the synchronous forces may need to be extended with
#mitigation algorithms similar to the current log buffer code to allow
#aggregation of multiple synchronous transactions if there are already
#synchronous transactions being flushed. Investigation of the performance of the
#current design is needed before making any decisions here.
ここで述べておきたいのは、この同期書き出し処理は、既に書き出し途中の同期トラン
ザクションが既にある際に複数の同期トランザクションの集約が行えるよう、現在のロ
グバッファコードと同様の緩和アルゴリズムを用いるよう拡張する必要があるのかもし
れないということです。現在の設計の性能についての調査・分析が、今後の判断を行う
前に必要です。

#The main concern with log forces is to ensure that all the previous checkpoints
#are also committed to disk before the one we need to wait for. Therefore we
#need to check that all the prior contexts in the committing list are also
#complete before waiting on the one we need to complete. We do this
#synchronisation in the log force code so that we don't need to wait anywhere
#else for such serialisation - it only matters when we do a log force.
ログ強制書き出しの主な関心事項は、対象チェックポイントの完了待ちの前にそれ以前
の全てのチェックポイントがディスクにコミットされていることを保証することです。
このため、完了させたいコンテキストの待ちの前に、コミットリストの以前のコンテキ
ストの全てが既に完了しているかどうかのチェックを行なっています。この同期処理は
ログ強制書き出しコード中で行なって、このような直列化の処理で他の箇所で順序待ち
合わせが発生しないようにしています ? ログ書き出しを行う際にのみ問題になること
であるためです。

#The only remaining complexity is that a log force now also has to handle the
#case where the forcing sequence number is the same as the current context. That
#is, we need to flush the CIL and potentially wait for it to complete. This is a
#simple addition to the existing log forcing code to check the sequence numbers
#and push if required. Indeed, placing the current sequence checkpoint flush in
#the log force code enables the current mechanism for issuing synchronous
#transactions to remain untouched (i.e. commit an asynchronous transaction, then
#force the log at the LSN of that transaction) and so the higher level code
#behaves the same regardless of whether delayed logging is being used or not.

残った唯一の難しい点は、ログ強制書き出しは、書き出そうとするシーケンス番号が現
在のコンテキストと同じものである場合を扱う必要が出てきていることです。これはつ
まり、CIL をフラッシュしてそれが完了するのを待つ必要がある可能性があるというこ
とです、これは現在のログ強制書き出しコードに対する簡単な追加であり、シーケンス
番号をチェックして必要に応じてプッシュするだけです。実際、ログ強制書き出しコー
ド内に現在のシーケンスチェックポイント書き出しコードを置くことにより、同期トラ
ンザクションに関する現在の機構 (つまり、非同期トランザクションをコミットして、
そのトランザクションの LSN の箇所までログの強制書き出しを行う処理) に触らずに
済み、上位のレベルのコードは遅延ロギングが使われているかどうかにかかわらず同じ
ように振る舞うことが可能になります。

#Delayed Logging: Checkpoint Log Space Accounting
遅延ロギング:チェックポイントログ容量の管理

#The big issue for a checkpoint transaction is the log space reservation for the
#transaction. We don't know how big a checkpoint transaction is going to be
#ahead of time, nor how many log buffers it will take to write out, nor the
#number of split log vector regions are going to be used. We can track the
#amount of log space required as we add items to the commit item list, but we
#still need to reserve the space in the log for the checkpoint.
トランザクションのログ容量の予約は、チェックポイントトランザクションの大きな問
題です。チェックポイントトランザクションがどこまで大きくなるかを事前に知ること
ができませんし、書き込みに必要なログバッファの数も、必要な分割ログベクタ容量の
数もわかりません。ログ容量の必要量はアイテムをアイテムリストにコミットする際に
把握することができますが、チェックポイント用のログの容量の予約は依然として必要
です。

#A typical transaction reserves enough space in the log for the worst case space
#usage of the transaction. The reservation accounts for log record headers,
#transaction and region headers, headers for split regions, buffer tail padding,
#etc. as well as the actual space for all the changed metadata in the
#transaction. While some of this is fixed overhead, much of it is dependent on
#the size of the transaction and the number of regions being logged (the number
#of log vectors in the transaction).
典型的なトランザクションでは、トランザクションのワーストケースでの容量消費に備
えて十分な容量を予約します。予約はログレコードヘッダ、トランザクションとリージョ
ンヘッダ、スプリットリージョンのヘッダ、テールパディングのためのバッファなどに
加え、トランザクション内の全ての変更されたメタデータが実際に使う領域が考慮され
ます。このうちの一部は固定のオーバヘッドですが、大部分はトランザクションサイズ
とログが取られたリージョンの数 (トランザクション内のログベクタの数) に依存する
ものです。

#An example of the differences would be logging directory changes versus logging
#inode changes. If you modify lots of inode cores (e.g. chmod -R g+w *), then
#there are lots of transactions that only contain an inode core and an inode log
#format structure. That is, two vectors totaling roughly 150 bytes. If we modify
#10,000 inodes, we have about 1.5MB of metadata to write in 20,000 vectors. Each
#vector is 12 bytes, so the total to be logged is approximately 1.75MB. In
#comparison, if we are logging full directory buffers, they are typically 4KB
#each, so we in 1.5MB of directory buffers we'd have roughly 400 buffers and a
#buffer format structure for each buffer - roughly 800 vectors or 1.51MB total
#space.  From this, it should be obvious that a static log space reservation is
#not particularly flexible and is difficult to select the "optimal value" for
#all workloads.
この違いを説明するために、ディレクトリ変更でのログと、inode 変更でのログを例に
取りましょう。大量の inode コアを変更した場合 (例えば  chmod -R g+w * で)、inode
 コアに対応するベクタと inode ログ形式の構造体ベクタのみを含んだトランザクショ
ンが多数生成されます。このとき概算で、この 2 つのベクタ組の指す値は 150 バイト
ほどになります。従って 10,000 個のinode を変更した場合、20,000 個のベクタに含
まれる 1.5MB ほどのメタデータを書き込む、ということになります。この各ベクタ自
体は 12 byte なので、ロギングされる総容量は概算 1.75MB ほどです。それに対して、
ディレクトリバッファ丸ごとのログを取る場合、典型的には 各 4KB 程度ほどになるの
で、1.5MB ほどのディレクトリバッファで概算 400 バッファほど、それに各バッファ
のバッファ形式を示す構造体を加えて概算 1.51MB の総容量で 800 ベクタということ
になります。このことから、静的なログ容量の割り当ては実用上十分な柔軟性を持つと
は言えず、全ての負荷に対する「最適値」を選定することは困難であることが見て取れ
るでしょう。

#Further, if we are going to use a static reservation, which bit of the entire
#reservation does it cover? We account for space used by the transaction
#reservation by tracking the space currently used by the object in the CIL and
#then calculating the increase or decrease in space used as the object is
#relogged. This allows for a checkpoint reservation to only have to account for
#log buffer metadata used such as log header records.
さらに、静的な領域割り当てを行った場合に、全体の予約のどのぐらいの割合が使われ
るかという問題もあります。現在のコードではトランザクション予約で使われた領域量
を CIL 内のオブジェクトで現在使われている量を追跡して計測し、オブジェクトが再
ロギングされる際の容量の増減量を計算しています。このためチェックポイントでの領
域予約としては、あとログヘッダレコードなどで用いられるログバッファメタデータの
計算だけを行えば良くなります。

#However, even using a static reservation for just the log metadata is
#problematic. Typically log record headers use at least 16KB of log space per
#1MB of log space consumed (512 bytes per 32k) and the reservation needs to be
#large enough to handle arbitrary sized checkpoint transactions. This
#reservation needs to be made before the checkpoint is started, and we need to
#be able to reserve the space without sleeping.  For a 8MB checkpoint, we need a
#reservation of around 150KB, which is a non-trivial amount of space.
しかしながら、ログメタデータに限った静的領域予約でさえ問題です。一般的にはログ
レコードヘッダは 1MB のログ容量の消費あたり少なくとも 16KB 使います (32KB あた
り 512byte)。この予約はチェックポイントが開始される前に行う必要があり、スリー
プなしに容量の予約が行えなければなりません。6MB のチェックポイントでは概算で
150KB が必要になりますが、これは些細な量とは言えません。

#A static reservation needs to manipulate the log grant counters - we can take a
#permanent reservation on the space, but we still need to make sure we refresh
#the write reservation (the actual space available to the transaction) after
#every checkpoint transaction completion. Unfortunately, if this space is not
#available when required, then the regrant code will sleep waiting for it.
静的予約では、ログの許可カウンタを操作する必要があります。この容量の予約を永続
的にすることは出来ますが、各チェックポイントトランザクション完了後に書き込み容
量予約 (トランザクションで使用できる実際の容量) を確実に更新しなければなりませ
ん。残念ながら、容量が要求時に提供できなければ、再ー許可コードは空き容量待ちで
スリープしてしまいます。

#The problem with this is that it can lead to deadlocks as we may need to commit
#checkpoints to be able to free up log space (refer back to the description of
#rolling transactions for an example of this).  Hence we *must* always have
#space available in the log if we are to use static reservations, and that is
#very difficult and complex to arrange. It is possible to do, but there is a
#simpler way.
この場合、ログ容量を解放するにはチェックポイントをコミットする必要があるため
(この例として、戻ってローリングトランザクションの説明を参照ください)、デッドロッ
クしてしまう問題があります。したがって、静的予約を用いる場合ログのための容量が
常に確保できるようにして置かなければならず、これは準備が難しくとても困難です。
仮にできたとしても、もっと簡単な方法があるのです。

#The simpler way of doing this is tracking the entire log space used by the
#items in the CIL and using this to dynamically calculate the amount of log
#space required by the log metadata. If this log metadata space changes as a
#result of a transaction commit inserting a new memory buffer into the CIL, then
#the difference in space required is removed from the transaction that causes
#the change. Transactions at this level will *always* have enough space
#available in their reservation for this as they have already reserved the
#maximal amount of log metadata space they require, and such a delta reservation
#will always be less than or equal to the maximal amount in the reservation.
より簡単な方法とは、CIL 内のアイテムで使われているログ容量の全体を追跡して、そ
の情報を用いて動的にログメタデータで必要になるログ容量容量を計算する方法です。
もしこのログメタデータ容量が、トランザクションのコミットの結果として CIL に挿
入された新しいメモリバッファによって変わるならば、必要な容量の差分を変更を起こ
したトランザクションから差し引きます。このレベルのトランザクションは、常に必要
な容量が予約確保時に得られます。というのは、要求するログメタデータの最大量を予
め確保してあり、上記の予約時の差分は常に容量予約の最大量以下となるためです。

#Hence we can grow the checkpoint transaction reservation dynamically as items
#are added to the CIL and avoid the need for reserving and regranting log space
#up front. This avoids deadlocks and removes a blocking point from the
#checkpoint flush code.
このようにして、チェックポイントトランザクション予約を、アイテムの CIL への追
加に動的に追従するものに拡大し、前線でログ容量の予約再確保が必要となる事態を避
けることができます。これにより、デッドロックは回避され、チェックポイントフラッ
シュコードからブロックを起こす箇所を削除しています。

#As mentioned early, transactions can't grow to more than half the size of the
#log. Hence as part of the reservation growing, we need to also check the size
#of the reservation against the maximum allowed transaction size. If we reach
#the maximum threshold, we need to push the CIL to the log. This is effectively
#a "background flush" and is done on demand. This is identical to
#a CIL push triggered by a log force, only that there is no waiting for the
#checkpoint commit to complete. This background push is checked and executed by
#transaction commit code.
前に述べたとおり、トランザクションはログのサイズの半分以上に伸びることはできま
せん。このため、予約部分が伸びていくと共に、許される最大のトランザクション長に
対する予約サイズのチェックも必要になります。最大値のしきい値を超えた場合には、CIL
 をログに書きこむ必要があります。これは処理としては「バックグラウンドのフラッ
シュ処理」と等価であり、必要に応じて実行されます。これはログ強制書き込みで起動
される CIL 書き込みと全く同じ処理であり、違いはチェックポイントコミットの完了
を待つ必要がない点のみです。このバックグラウンドのフラッシュ処理のチェックと実
行は、トランザクションコミットコードで行われます。

#If the transaction subsystem goes idle while we still have items in the CIL,
#they will be flushed by the periodic log force issued by the xfssyncd. This log
#force will push the CIL to disk, and if the transaction subsystem stays idle,
#allow the idle log to be covered (effectively marked clean) in exactly the same
#manner that is done for the existing logging method. A discussion point is
#whether this log force needs to be done more frequently than the current rate
#which is once every 30s.
CIL にアイテムが残っている状態でトランザクションサブシステムが暇になった場合、xfssy
ncd が定期的に起動するログ強制書き込みでアイテムをフラッシュします。このログ強
制書き込みは CIL をディスクに書き込み、さらにトランザクションサブシステムがそ
れでもまだ暇ならば、アイドルログを挿入しておきます (等価的には、クリーンとマー
クしたのと同様)。これは、既存のログ処理方法と全く同じように行われます。ここで
の検討点は、このログ強制書き込みは、現在の 30 秒おきの頻度より頻繁に行う必要が
あるか否か、です。
<!-- TODO: 二文目がわからん -->


#Delayed Logging: Log Item Pinning
遅延ロギング:ログアイテムのピン止め

#Currently log items are pinned during transaction commit while the items are
#still locked. This happens just after the items are formatted, though it could
#be done any time before the items are unlocked. The result of this mechanism is
#that items get pinned once for every transaction that is committed to the log
#buffers. Hence items that are relogged in the log buffers will have a pin count
#for every outstanding transaction they were dirtied in. When each of these
#transactions is completed, they will unpin the item once. As a result, the item
#only becomes unpinned when all the transactions complete and there are no
#pending transactions. Thus the pinning and unpinning of a log item is symmetric
#as there is a 1:1 relationship with transaction commit and log item completion.

現在ログアイテムは、トランザクションコミット中の、アイテムがまだロックされてい
る間はピン止めされています。ピン止め処理はアイテムの書式変換の直後からになりま
す (実は、アイテムがアンロックされる前ならいつ行っても構いません)。この処理の
結果、アイテムはログバッファにコミットされる各々のトランザクションに対して一回
ピン止めされます。このため、ログバッファでの再ロギング対象のアイテムは、ダーティ
になっている全ての未処理のトランザクションに対応したピン数を持ちます。各トラン
ザクションの完了時に、アイテムのピンが一個削除されます。この結果、アイテムのピ
ンが全部外れた状態になるのは全てのトランザクションが完了し、仕掛り中のトランザ
クションがなくなった時です。このため、ログアイテムのピン止めとピン削除は対称な
処理になります。これは、トランザクションコミットとログアイテム完了の間に 1:1
の関係があるためです。

#For delayed logging, however, we have an asymmetric transaction commit to
#completion relationship. Every time an object is relogged in the CIL it goes
#through the commit process without a corresponding completion being registered.
#That is, we now have a many-to-one relationship between transaction commit and
#log item completion. The result of this is that pinning and unpinning of the
#log items becomes unbalanced if we retain the "pin on transaction commit, unpin
#on transaction completion" model.
一方遅延ロギングでは、トランザクションコミットと完了との関係は非対称なものとな
ります。オブジェクトが CIL で再ロギングされるたびに、対応する完了処理が登録さ
れないままコミット処理が走ります。このことから、トランザクションコミットとログ
アイテム完了との間に多対一の関係が出てきます。このため、ログアイテムのピン止め
とピン削除も、「トランザクションコミット時にピン止め+トランザクション完了時に
ピン削除」を守ろうとすると非対称になります。

#To keep pin/unpin symmetry, the algorithm needs to change to a "pin on
#insertion into the CIL, unpin on checkpoint completion". In other words, the
#pinning and unpinning becomes symmetric around a checkpoint context. We have to
#pin the object the first time it is inserted into the CIL - if it is already in
#the CIL during a transaction commit, then we do not pin it again. Because there
#can be multiple outstanding checkpoint contexts, we can still see elevated pin
#counts, but as each checkpoint completes the pin count will retain the correct
#value according to it's context.
ピン止め・ピン削除の対称性を維持するためには、アルゴリズムを「CIL への挿入時に
ピン止め+チェックポイント完了時にピン削除」に変更する必要があります。言い換え
れば、ピン止めとピン削除は、チェックポイントコンテキストを挟んで対称、となりま
す。オブジェクトが初めて CIL に挿入された際にピン止めしなければならず、トラン
ザクションコミット中に既に CIL 内にある場合には再度ピンどめしてはいけません。
複数の仕係り中のチェックポイントが存在する可能性があるため、ピン数が増加するの
を目撃することは依然としてありますが、各チェックポイントの完了時にはピン数はコ
ンテキストに沿った正しい値を維持します。

#Just to make matters more slightly more complex, this checkpoint level context
#for the pin count means that the pinning of an item must take place under the
#CIL commit/flush lock. If we pin the object outside this lock, we cannot
#guarantee which context the pin count is associated with. This is because of
#the fact pinning the item is dependent on whether the item is present in the
#current CIL or not. If we don't pin the CIL first before we check and pin the
#object, we have a race with CIL being flushed between the check and the pin
#(or not pinning, as the case may be). Hence we must hold the CIL flush/commit
#lock to guarantee that we pin the items correctly.
話をもう少し複雑にしましょう。このピン数のチェックポイントレベルのコンテキスト
とは、一つのアイテムに対するピン止め操作処理を CIL コミット/フラッシュロック取
得下で行わなければならないことを意味します。ロック範囲外でオブジェクトをピン止
めした場合、ピン数が関連付けられるコンテキストがどれであるかの保証ができません。
これは、そのアイテムのピン止め処理が、そのアイテムが現在の CIL 内にあるかどう
かに依存していることに起因します。もし、オブジェクトをチェックしてピンどめする
前に CIL をピンどめして置かなければ、まさにフラッシュされようとしている CIL で
チェック処理とピン止め処理 (またはひょっとするとピン削除) との競合が発生します。
従って、CIL フラッシュ/コミットロックを取得して、アイテムが正しくピン止めされ
ることを保証しなければいけません。

#Delayed Logging: Concurrent Scalability
遅延ロギング:並列動作の拡張性

#A fundamental requirement for the CIL is that accesses through transaction
#commits must scale to many concurrent commits. The current transaction commit
#code does not break down even when there are transactions coming from 2048
#processors at once. The current transaction code does not go any faster than if
#there was only one CPU using it, but it does not slow down either.
CIL の基本的要求事項として、トランザクションコミットを経由したアクセスは、多く
の並行コミットを許すだけの拡張性を持たなければなりません。現在のトランザクショ
ンコミットコードは、トランザクションが 2048 プロセッサから一度に来た場合でもお
かしくなりません。現在のトランザクションコードは、使っているのが一つのプロセッ
サのみだったときにより速くなるようなことはありませんが、より遅くなることもあり
ません。

#As a result, the delayed logging transaction commit code needs to be designed
#for concurrency from the ground up. It is obvious that there are serialisation
#points in the design - the three important ones are:
この結果、遅延ロギングでのトランザクションコミットでは、根元から並列性を狙って
設計される必要があります。設計上シリアライズを行う箇所があるのは明らかでしょう。
その内の重要な 3 つは以下のものです。

#	1. Locking out new transaction commits while flushing the CIL
#	2. Adding items to the CIL and updating item space accounting
#	3. Checkpoint commit ordering
	1. CIL をフラッシュする間新規のトランザクションをロックにより受け付け
ない。
	2. CIL にアイテムを加え、容量管理を更新する
	3. チェックポイントコミットの順序付け

#Looking at the transaction commit and CIL flushing interactions, it is clear
#that we have a many-to-one interaction here. That is, the only restriction on
#the number of concurrent transactions that can be trying to commit at once is
#the amount of space available in the log for their reservations. The practical
#limit here is in the order of several hundred concurrent transactions for a
#128MB log, which means that it is generally one per CPU in a machine.
トランザクションコミットと CIL フラッシュとの干渉に目を向ければ、多対一の干渉
が発生することが明白になります。つまり、並行して一回にコミットに投入可能なトラ
ンザクション数を制限するのは、ログ用に予約可能な容量のみです。ここでの現実的な
制限は、マシンあたり 1 CPU あたりでの一般的な値である 128MB のログで数百の桁の
並行トランザクション程度になります。

<!-- several なので、1500 位である可能性はあり -->

#The amount of time a transaction commit needs to hold out a flush is a
#relatively long period of time - the pinning of log items needs to be done
#while we are holding out a CIL flush, so at the moment that means it is held
#across the formatting of the objects into memory buffers (i.e. while memcpy()s
#are in progress). Ultimately a two pass algorithm where the formatting is done
#separately to the pinning of objects could be used to reduce the hold time of
#the transaction commit side.
トランザクションコミットがフラッシュを行うために必要な時間は、比較的長時間です。
ログアイテムのピン止めは CIL フラッシュを行なっている間は行いつづける必要があ
り、現時点ではそれはオブジェクトのフォーマットを行なってメモリバッファに格納す
る (つまり memcpy() を行う間) までピンどめしていることを意味します。究極的には
フォーマットをオブジェクトのピン止めとは独立に行う 2 パスのアルゴリズムを使っ
てトランザクションコミット側のホールド時間を削減することが可能かもしれません。

#Because of the number of potential transaction commit side holders, the lock
#really needs to be a sleeping lock - if the CIL flush takes the lock, we do not
#want every other CPU in the machine spinning on the CIL lock. Given that
#flushing the CIL could involve walking a list of tens of thousands of log
#items, it will get held for a significant time and so spin contention is a
#significant concern. Preventing lots of CPUs spinning doing nothing is the
#main reason for choosing a sleeping lock even though nothing in either the
#transaction commit or CIL flush side sleeps with the lock held.
トランザクションコミット側のピン止めのおこない手の潜在的な数のため、ロックは必
ずスリープタイプのロックでなければなりません。CIL フラッシュがロックを取得した
場合、他の全ての CPU が CIL ロックを待ってスピンする事態は避けたい為です。CIL
のフラッシュは数千のログアイテムからなるリストを探索する処理を含みますから、か
なりの時間の間ロックが保持され、スピンの競合は重大な懸案事項になりえます。トラ
ンザクションコミット側と CIL フラッシュ側のいずれにもロックを取ったままスリー
プする要因がない状況でも、スリープタイプのロックを選択して多数の CPU がスピン
を行なって何もしない状況を避けることが、この選択の主な理由です。

#It should also be noted that CIL flushing is also a relatively rare operation
#compared to transaction commit for asynchronous transaction workloads - only
#time will tell if using a read-write semaphore for exclusion will limit
#transaction commit concurrency due to cache line bouncing of the lock on the
#read side.
さらに留意して欲しいのは、CIL フラッシュは非同期のトランザクション負荷にともな
うトランザクションコミットに比べると比較的頻度の低い操作であるという点です。排
他にリード/ライトセマフォアを使うことがロック読み出し側のキャッシュラインバウ
ンスにより並列度を低下させるかどうかは今後明らかになっていくと思います。

#The second serialisation point is on the transaction commit side where items
#are inserted into the CIL. Because transactions can enter this code
#concurrently, the CIL needs to be protected separately from the above
#commit/flush exclusion. It also needs to be an exclusive lock but it is only
#held for a very short time and so a spin lock is appropriate here. It is
#possible that this lock will become a contention point, but given the short
#hold time once per transaction I think that contention is unlikely.
二番目のシリアライズ箇所はトランザクションコミット側で、CIL にアイテムを挿入す
る処理です。トランザクションはこのコードに並列に飛び込むことができるため、CIL
は上記のコミット/フラッシュの排他とは別に保護する必要があります。これには排他
ロックが必要ですが、極めて短時間のみ保持されるためこの場合はスピンロックが適し
ています。このロックが競合点になる可能性はありますが、トランザクション毎の保持
時間が短いことを考慮すると可能性は低いと考えています。

#The final serialisation point is the checkpoint commit record ordering code
#that is run as part of the checkpoint commit and log force sequencing. The code
#path that triggers a CIL flush (i.e. whatever triggers the log force) will enter
#an ordering loop after writing all the log vectors into the log buffers but
#before writing the commit record. This loop walks the list of committing
#checkpoints and needs to block waiting for checkpoints to complete their commit
#record write. As a result it needs a lock and a wait variable. Log force
#sequencing also requires the same lock, list walk, and blocking mechanism to
#ensure completion of checkpoints.
最後のシリアライズ箇所は、チェックポイントコミットとログ強制書き出しの順序付け
の一部として実行されるチェックポイントコミットレコードの順序付けコードです。CIL
 フラッシュを起動したコードパス (つまり、ログ強制書き出しを起動した処理) は、
全てのログベクタをログバッファに書き込んだ後、コミットレコードを書きこむ前に順
序付けのループに入ります。このループはコミット中のチェックポイントのリストを探
索し、チェックポイントが自分のコミットレコード書き込みを完了するまでロックを保
持して待つ必要があります。この結果、この処理はロックが必要で、変数値を待ち合わ
せることになります。ログの強制書き込み順序制御も同じロック、リスト探索、チェッ
クポイントの完了をロックを保持したまま待ち合わせる処理を必要とします。

#These two sequencing operations can use the mechanism even though the
#events they are waiting for are different. The checkpoint commit record
#sequencing needs to wait until checkpoint contexts contain a commit LSN
#(obtained through completion of a commit record write) while log force
#sequencing needs to wait until previous checkpoint contexts are removed from
#the committing list (i.e. they've completed). A simple wait variable and
#broadcast wakeups (thundering herds) has been used to implement these two
#serialisation queues. They use the same lock as the CIL, too. If we see too
#much contention on the CIL lock, or too many context switches as a result of
#the broadcast wakeups these operations can be put under a new spinlock and
#given separate wait lists to reduce lock contention and the number of processes
#woken by the wrong event.
これら 2 つの順序処理は、待ち合わせるイベントが別々であるのにもかかわらずこの
メカニズムを使用可能です。チェックポイントコミットレコード順序処理はチェックポ
イントコンテキストがコミット LSN (コミットレコード書き込みの完了によって得られ
ます) を取得するまで待つ必要があり、一方ログ強制書き出し順序処理は以前のチェッ
クポイントコンテキストがコミット中リストから削除される (つまり完了する) まで待
つ必要があります。単純な待ち合わせ変数とブロードキャストの目覚まし割込み (爆音
の行進) がこの 2 つのシリアライズキューの実装に使われています。これらは CIL と
同じロックも用いています。今後 CIL ロックでの競合が頻繁に発生する状況になるか、
ブロードキャスト目覚まし割り込みの結果コンテキストスイッチの発生が多すぎる状況
となったばあい、これらの処理を新しいスピンロックの制御下に移して独立した待ち合
わせリストを設置することにより、ロック競合の頻度を抑え、誤ったイベントで起こさ
れるプロセスの数をへらすことも可能です。


#Lifecycle Changes
ライフサイクルの変更

#The existing log item life cycle is as follows:
従来の方法でのログアイテムのライフサイクルは以下の通りです。

#	1. Transaction allocate
#	2. Transaction reserve
#	3. Lock item
#	4. Join item to transaction
#		If not already attached,
#			Allocate log item
#			Attach log item to owner item
#		Attach log item to transaction
#	5. Modify item
#		Record modifications in log item
#	6. Transaction commit
#		Pin item in memory
#		Format item into log buffer
#		Write commit LSN into transaction
#		Unlock item
#		Attach transaction to log buffer
#
#	<log buffer IO dispatched>
#	<log buffer IO completes>
#
#	7. Transaction completion
#		Mark log item committed
#		Insert log item into AIL
#			Write commit LSN into log item
#		Unpin log item
#	8. AIL traversal
#		Lock item
#		Mark log item clean
#		Flush item to disk
#
#	<item IO completion>
#
#	9. Log item removed from AIL
#		Moves log tail
#		Item unlocked
	1. トランザクション割り当て
	2. トランザクションリザーブ
	3. アイテムをロックする
	4. トランザクションにアイテムを追加する
		もしアタッチ未であるなら
			ログアイテムを割り当て
			ログアイテムをオーナアイテムにアタッチする
		ログアイテムをトランザクションにアタッチする
	5. アイテムを変更する
		ログアイテムの変更を記録する
	6. トランザクションのコミット
		メモリ上のアイテムをピン止めする。
		ログベクタ+バッファ向けにフォーマット変換する
		コミットされた LSN をトランザクションに書きこむ
		アイテムのロックを解除する
		トランザクションをログバッファにアタッチする
		
	<ログバッファ IO 投入>
	<ログバッファ IO 完了>

	7. チェックポイント完了
		ログアイテムをコミット済みとマークする。
		アイテムを AIL に投入する
			コミットした LSN をログアイテムに書き込む
		ログアイテムのピン止めを開放
	8. AIL 探索
		アイテムをロック
		ログアイテムをクリーンとマークする
		ディスクに書きこんでバッファを解放する

	<ログ操作 IO 完了>

	9. ログアイテムを AIL から削除する
		ログ最後に移動する
		ログアイテムをアンロックする

#Essentially, steps 1-6 operate independently from step 7, which is also
#independent of steps 8-9. An item can be locked in steps 1-6 or steps 8-9
#at the same time step 7 is occurring, but only steps 1-6 or 8-9 can occur
#at the same time. If the log item is in the AIL or between steps 6 and 7
#and steps 1-6 are re-entered, then the item is relogged. Only when steps 8-9
#are entered and completed is the object considered clean.
基本的には、ステップ 1 から 6 はステップ 7 とは独立な処理で、ステップ 7 は更に
ステップ 8 から 9 とは独立の処理です。ステップ 1 から 6 またはステップ 8 と 9
でのロックはステップ 7 の処理と同時に実行可能ですが、ステップ 1-6 と 8-9 は、
一方しか動作できません。もしログアイテムが AIL にあるか、ステップ 6 と 7 の間
にあってステップ 1-6 が歳入された場合、アイテムは再ロギングされます。ステップ
8-9 に移行して完了するのは、オブジェクトがクリーンであると判断された場合だけで
す。

#With delayed logging, there are new steps inserted into the life cycle:
遅延ロギング下では、いくつかの新しい手順がライフサイクルに挿入されます。

#	1. Transaction allocate
#	2. Transaction reserve
#	3. Lock item
#	4. Join item to transaction
#		If not already attached,
#			Allocate log item
#			Attach log item to owner item
#		Attach log item to transaction
#	5. Modify item
#		Record modifications in log item
#	6. Transaction commit
#		Pin item in memory if not pinned in CIL
#		Format item into log vector + buffer
#		Attach log vector and buffer to log item
#		Insert log item into CIL
#		Write CIL context sequence into transaction
#		Unlock item
#
#	<next log force>
#
#	7. CIL push
#		lock CIL flush
#		Chain log vectors and buffers together
#		Remove items from CIL
#		unlock CIL flush
#		write log vectors into log
#		sequence commit records
#		attach checkpoint context to log buffer
#
#	<log buffer IO dispatched>
#	<log buffer IO completes>
#
#	8. Checkpoint completion
#		Mark log item committed
#		Insert item into AIL
#			Write commit LSN into log item
#		Unpin log item
#	9. AIL traversal
#		Lock item
#		Mark log item clean
#		Flush item to disk
#	<item IO completion>
#	10. Log item removed from AIL
#		Moves log tail
#		Item unlocked
	1. トランザクション割り当て
	2. トランザクションリザーブ
	3. アイテムをロックする
	4. トランザクションにアイテムを追加する
		もしアタッチ未であるなら
			ログアイテムを割り当て
			ログアイテムをオーナアイテムにアタッチする
		ログアイテムをトランザクションにアタッチする
	5. アイテムを変更する
		ログアイテムの変更を記録する
	6. トランザクションのコミット
		メモリ上のアイテムを CIL 内でピン止めされていなかった場合、ピ
ン止めする。
		ログベクタ+バッファ向けにフォーマット変換する
		ログベクタ+バッファをログアイテムにアタッチする
		ログアイテムを CIL に挿入する
		CIL コンテンツシーケンスをトランザクションに書きこむ
		アイテムのロックを解除する
		
	<次のログ強制書き込み>
		
	7. CIL プッシュ
		CIL flush をロックする
		ログベクタとバッファを一緒にチェインする
		CIL からアイテムを削除する
		CIL flush のロックを解除する
		ログベクタをログに書きこむ
		コミットレコードの順番を確認する
		チェックポイントコンテキストをログバッファにアタッチする

	<ログバッファ IO 投入>
	<ログバッファ IO 完了>

	8. チェックポイント完了
		ログアイテムをコミット済みとマークする。
		アイテムを AIL に投入する
			コミットした LSN をログアイテムに書き込む
		ログアイテムのピン止めを開放
	9. AIL 探索
		アイテムをロック
		ログアイテムをクリーンとマークする
		ディスクに書きこんでバッファを解放する
	<ログ操作 IO 完了>
	10. ログアイテムを AIL から削除する
		ログ最後に移動する
		ログアイテムをアンロックする

#From this, it can be seen that the only life cycle differences between the two
#logging methods are in the middle of the life cycle - they still have the same
#beginning and end and execution constraints. The only differences are in the
#committing of the log items to the log itself and the completion processing.
#Hence delayed logging should not introduce any constraints on log item
#behaviour, allocation or freeing that don't already exist.
このことから、2 つのロギング手法のライフサイクルの違いは、ライフサイクルの真ん
中辺だけであることが見て取れるでしょう。始まりと終わり、および実行時の制限につ
いては同じです。違いはログアイテムをログにコミットするところとその終了処理だけ
です。このことから、遅延ロギングはログアイテムの挙動に何らの新規の制約を導入す
るものではなく、すでに存在しているもの以外の割り当てや開放を行うものではないも
のとなっているはずです。

#As a result of this zero-impact "insertion" of delayed logging infrastructure
#and the design of the internal structures to avoid on disk format changes, we
#can basically switch between delayed logging and the existing mechanism with a
#mount option. Fundamentally, there is no reason why the log manager would not
#be able to swap methods automatically and transparently depending on load
#characteristics, but this should not be necessary if delayed logging works as
#designed.
この遅延ロギングインフラストラクチャでの影響ゼロの「インサーション」の結果、お
よびディスク上のフォーマットの変更を避けた内部構造設計の結果、遅延ロギングと既
存メカニズムをマウントオプションによって切り替えることが可能になっています。
基本的には、ログマネージャで負荷の性質に対応して自動的かつ透過的にロギング手段
を切り替えてはいけない理由はないのですが、設計通りに遅延ロギングが動作するなら
このような切り替えは必要にはならないはずです。

---------- >8 ---------- >8

-- 
Seiji Kaneko


JF-gofer メーリングリストの案内