[Firestore] 9편- 파이어스토어 캐시
Firestore 캐싱동작
기본적인 동작과 새로 깨달은 점을 정리한다.
일단 캐싱된 데이터는 앱 데이터베이스에 저장되어 앱 설정에서 캐시삭제말고 앱 데이터를 통해 모두 밀어버리던지, 코드로 삭제를 제어해야 한다.
Firestore 문서 캐싱 데이터가 디바이스의 어디에 저장되는지 궁금해서 구글링해본 결과
데이터베이스에 저장한다.
문서 관련 테스트
주의: 오프라인 지속성 버튼은 write,read작업이 한번이라도 들어간 상태에서 바꾸면 크래쉬 난다.
코드, xml
오프라인 지속성
쿼리(컬렉션 쿼리, 컬렉션 그룹쿼리), 문서 get, Transaction, Batched Write 가 기본 동작이 조금씩 다른점을 포함하여 오프라인 지속성 개념까지 챙기려면 약간 복잡해진다.
오프라인 지속성은 안드로이드, 애플, 웹 앱에서만 지원된다.
안드로이드의 경우 Firestore의 default 오프라인 지속성은 true 상태이다.
//kotlin + ktx
val settings = firestoreSettings {
isPersistenceEnabled = false
}
db.firestoreSettings = settings
중간중간 지속성을 변경하면서 write,read등등을 할 수 있을 줄알았는데 Firebase 인스턴스화후 read,write작업을 한번이상이라도 한 상태에서 지속성 변경이 들어가면 앱이 crash난다.
만약 오프라인 지속성이 다르게 설정된 파이어스토어 변수를 두개 참조하더라도 둘중 하나인 mFirestore로 데이터베이스 작업을 한번이라도 시작하면 mFirestore, mFirestoreOffLineFalse 모두 지속성 변경을 하면 앱이 크래쉬된다.
캐시 사이즈 구성
오프라인 지속성이 가능해지면
오프라인 상태일때 문서를 받기 위한 도움 목적으로 백엔드로부터 받은 모든 문서를 캐시에 저장한다.
정기적으로 오래되고 사용되지 않는 문서들을 청소한다.
다른 캐시 사이즈를 구성하거나 이런 청소 작업을 비활성하게 구성할 수도 있다.
// The default cache size threshold is 100 MB. Configure "setCacheSizeBytes"
// for a different threshold (minimum 1 MB) or set to "CACHE_SIZE_UNLIMITED"
// to disable clean-up.
val settings = FirebaseFirestoreSettings.Builder()
.setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED)
.build()
db.firestoreSettings = settings
default 100MB, 최소 1MB
"CACHE_SIZE_UNLIMITED"를 사용하여 캐시를 정리하지 않고 계속 쌓이게 구성가능
FirebaseFirestoreSettings.Builder#setCacheSizeBytes 메서드 설명
공간이 부족한 경우 가장 마지막으로 사용된 데이터를 삭제한다.
default로 컬렉션당 100MB까지 가능하다. 최소는 1MB
FirebaseFirestoreSettings
특정 문서 참조로 문서 읽기
get()(=get(Source.DEFAULT)) , get(Source.SERVER), get(Source.CACHE)
공통 캐싱 동작 - 서버에서 문서를 얻어올때마다 캐시에 새로운 데이터로 업데이트 한다.
그러므로 get(Source.CACHE)를 제외한 get()(=get(Source.DEFAULT)) , get(Source.SERVER)에서 task가 성공했으면
(서버에서 문서를 읽는 것에 성공했으면) 디바이스 캐시에 새로운 데이터로 업데이트 된다.
- get(Source.CACHE) - 서버를 무시하고 무조건 캐시에서 문서 데이터 가져옴. 디바이스가 온라인이던, 오프라인이던 디바이스에서 가져오므로 동작이 같다.
앱 최초 실행 -> 컬렉션1/문서1 get(Source.CACHE)진행하면 네트워크 상태 상관없이 task 실패
(이전에 캐싱되어있던것이 없음)
(이때 예외출력 - 캐시로부터 문서를 가져올 수 없습니다. 서버에 있을 수도 있습니다.
캐시옵션을 제외하고 시도해주세요)
앱 최초 실행 -> 컬렉션1/문서1 get()또는get(Source.SERVER) 진행 -> 디바이스 온라인 상태이고 보안 룰에 걸리지 않았다면 무조건 task 성공 -> 컬렉션1/문서1의 문서경로의 문서 상태를 디바이스 캐시에 저장
(문서가 없으면 없는 정보 저장, 있으면 문서 저장) -> 컬렉션1/문서1 get(Source.CACHE) -> 네트워크 상태 상관없이 task 성공 (이전에 캐싱되어있던 정보출력)
문서가 없어도 문서가 없다는 정보로 저장.
문서가 있지만 데이터가 없어도 빈문서라고 정보 저장.
데이터를 들고있는 문서도 정보 저장.
즉 문서 유무 상관없이 특정 문서 경로를 한번이라도 서버에서 read 했다면 코드로 캐시를 밀거나, 앱 데이터를 삭제하지 않는 이상 무조건 캐시에서 읽을 수 있음
- get(Source.SERVER) - 캐시 완전 무시. 서버에서만 가져옴.
디바이스 온라인 상태 + 보안 룰 안걸리면
무조건 task 성공
디바이스 오프라인 상태 - 무조건 task 실패
무조건 task 실패지만 예외 출력을 다르게 해줌
한번도 캐싱된 적이 없는 경우- 오프라인이라서 서버에서 문서를 받을 수 없다
캐싱된 적이 있는 경우- 오프라인이라서 서버에서 문서를 받을 수 없지만, 캐시에 저장된 것이 있어 이를 이용할 수 있다.
- get(Source.DEFAULT) - 메서드가 호출될 때 디바이스 연결부터 체크. 디바이스가 온라인이면 get(Source.SERVER)로 진행되고, 오프라인이면 get(Source.DEFAULT)로 진행된다.
디바이스 온라인 상태 - 무조건 서버에서 들고옴. 보안 룰만 안걸리면 무조건 task 성공
디바이스 오프라인 상태 - 한번이라도 그 경로가 캐싱된적(read성공)이 없으면 task 실패, 이외에 무조건 task 성공
특정 문서 참조로 문서에 쓰기 set(), update()
서버에서 경로를 통해 문서를 read할때만 캐싱이 진행되는 줄 알았는데, write작업때도 캐싱이 진행된다.
테스트 할때마다 코드를 바꿔가며 앱을 재실행해대니 불편해서 테스트 코드를 따로 짜봤더니 깨달은 내용이다.
기본적으로 오프라인 상태에서 set(),update()메서드는 task가 시작되지 않고 큐에 들어가있다가 네트워크가 재연결 되는 순간에 작업이 시작된다.
오프라인 상태에서 문서를 write하면 백엔드에는 네트워크가 재연결 되는 순간에 써지지만, 디바이스 캐시에는 바로 업데이트 한다.
--> 해당 시나리오
백엔드에 collection/document3 문서가 test를 가지고있다(편의상 이런식으로 가정)
온라인 상태에서 get(서버옵션) 또는 get(디폴트)로 문서를 가져온다.
캐시에는 이제 test인 문서 상태로 업데이트 된다.
오프라인 상태에서 데이터를 helloworld로 write(set,update)한다.
백엔드에는 온라인 상태가 되면 써지므로 아무일도 일어나지 않는다.
그러나 캐시에는 업데이트 하므로 get(캐시옵션)으로 읽어오면
백엔드에는 네트워크가 온라인이 되면 write되지만 캐시에는 helloworld로 업데이트 된 것을 가져온다.
스냅샷에서 변경작업이 진행중인 문서를 읽었다는 pending write true 플래그가 담겨있다.
이제 디바이스를 온라인으로 전환하면 task가 시작되고 백엔드에 써진다.
트랜잭션으로 read, write한 모든 문서들은 디바이스의 캐시에 업데이트 되지 않는다.
- 트랜잭션으로 read
트랜잭션으로 읽은 문서경로들 대한 문서들은 캐싱되지 않는다.
아래의 코드들은 단순한 테스트 코드로 트랜잭션의 본 목적에 맞지 않을 수도 있다.
onCreate에서 document100, document1000, document10000 문서 세개를 트랜잭션으로 동시에 읽고
이후에 DocumentReference#get(Source.CACHE)로 세문서를 읽어봤는데 캐싱된 것이 없다고 출력되었다.
onCreate 코드
트랜잭션은 오프라인시 실패이므로 디바이스 온라인 상태에서 테스트 하면
onCreate에서 트랜잭션 진행되고 트랜잭션 task 완료가 나올 때 버튼을 눌러 세 문서를 읽으면 캐싱되어 있지 않다.
- 트랜잭션으로 write
위와 반대로 onCreate의 트랜잭션에서 문서 세개를 쓰고
버튼의 세 문서를 캐시에서 read해보면 캐싱되지 않는다.
Batched Write 로 write한 문서들은 디바이스 캐시에 업데이트 된다
onCreate메서드에서 시작과 함께 Batch Wrtie 작업 진행
디바이스 온라인 상태에서 앱 실행
뒤이어 버튼으로 세문서를 get(캐시)로 읽어보면 캐시가 존재한다.
디바이스 오프라인 시의 DocumentReference#set, update 의 동작과 같게 batched write도 오프라인시 task를 큐에 대기시켜두고 오프라인이지만 디바이스 캐시에는 바로 업데이트 한다.
콘솔의 데이터와, 앱 데이터를 모두 지우고, 앱을 강제종료나 최근앱으로 날려버리고 다시 오프라인에서 앱을 실행한다.
오프라인이여서 onCreate의 batch write task가 시작되지 않아 완료리스너 메서드가 호출되지 않는다.
1표시 밑은 버튼을 눌러 캐시된 문서가 있는지 테스트해보면 오프라인인데 디바이스의 캐시에는 업데이트가 됨을 볼수 있다. 2표시 밑은 네트워크 연결 후 batch write task가 시작되고 끝나는 것을 볼 수 있다.
또한 DocumentReference#set, update 의 오프라인 write 작업시 백엔드에 처리가 안되어서
pending write 플래그(작업이 보류된 문서인지)가 true이다.
쿼리 또한 해당되는 문서들이 서버에서 가져와질 때마다 디바이스 캐시에 업데이트 된다.