I was wondering why is the eternal storage approach (first code snippet) still used? I know it's meant to make contracts upgradable but it seems too expensive to me.

```
contract Storage {

   mapping(bytes32 => uint256)    private uIntStorage;
   mapping(bytes32 => string)     private stringStorage;
   mapping(bytes32 => address)    private addressStorage;
   mapping(bytes32 => bytes)      private bytesStorage;
   mapping(bytes32 => bool)       private boolStorage;
   mapping(bytes32 => int256)     private intStorage;

   function getAddress(bytes32 _key) public view returns (address) {
       return addressStorage[_key];
   }

   function getUint(bytes32 _key) public view returns (uint) {
       return uIntStorage[_key];
   }

   function getString(bytes32 _key) public view returns (string) {
       return stringStorage[_key];
   }

   function getBytes(bytes32 _key) public view returns (bytes) {
       return bytesStorage[_key];
   }

   function getBool(bytes32 _key) public view returns (bool) {
       return boolStorage[_key];
   }

   function getInt(bytes32 _key) public view returns (int) {
       return intStorage[_key];
   }

   function setAddress(bytes32 _key, address _value) public {
       addressStorage[_key] = _value;
   }

   function setUint(bytes32 _key, uint _value) public {
       uIntStorage[_key] = _value;
   }

   function setString(bytes32 _key, string _value) public {
       stringStorage[_key] = _value;
   }

   function setBytes(bytes32 _key, bytes _value) public {
       bytesStorage[_key] = _value;
   }

   function setBool(bytes32 _key, bool _value) public {
       boolStorage[_key] = _value;
   }

   function setInt(bytes32 _key, int _value) public {
       intStorage[_key] = _value;
   }
 }

```

Consider this struct. 

```
 struct Test {
   address user;
   uint96 amount;
   uint48 timestamp;
   uint48 expiry;
 }

```

Saving one struct element to a mapping would need 3 storage slots:
1 for the mapping key
1 for user and amount
1 for timestamp and expiry

Now consider saving the same struct in the eternal storage shown before. 
We would need 4 slots for the mapping keys and 4 slots for the actual values.

So 3 slots vs 8 slots. 

Keeping in mind the a SSTORE operations costs 20000 gas, the struct method costs 60k gas vs 160k gas of the eternal storage.

Supposing my calculations are correct, this seems too much of a difference to me, especially from a customer perspective. So why is this method still used?

EDIT: As Jesse clarified below, mapping keys do not cost any SSTORE operation. Even deleting them from the calculation, we would still have double costs for the eternal storage, 4 slots, compared to the struct, 2 slots. The difference would be even bigger for more complex structs.