-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RealmManager 개선 #107
base: dev
Are you sure you want to change the base?
RealmManager 개선 #107
Conversation
realmSerialQueue.async { | ||
let realm = try! Realm(configuration: configuration) | ||
|
||
try? realm.write { | ||
realm.add(entity) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
쓰기작업은 백그라운드 스레드에서 수행하도록 했어요!
공식문서를 보면 Realm에의 쓰기는 특히 백그라운드 스레드에서 진행하라고 권고하라고 되어있어서 적용해봤어요..
공식문서 설명
Avoid writes on the UI thread if you write on a background thread:
You can write to a realm from any thread, but there can be only one writer at a time. Consequently, write transactions block each other. A write on the UI thread may result in your app appearing unresponsive while it waits for a write on a background thread to complete. If you are using Device Sync, avoid writing on the UI thread as Sync writes on a background thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오호,, 여기서 제드가 말씀하신 백그라운드 스레드는 메인 스레드를 제외한 다른 스레드를 말씀하신거죠?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵넵!! 하나 빠뜨렸는데 global queue가 아닌 serial queue로 직접 명시했더라구요
왜그런가 생각해보니까 thread-confined.. 즉 하나의 스레드에서만 처리해야 되니까 serial queue를 사용해야 동일 스레드에서의 작업처리가 보장되서 그런거 같네요!
|
||
let realm = try! Realm(configuration: self.configuration, queue: self.realmSerialQueue).freeze() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분이 본문에서 언급한 Realm 객체를 frozen object
형태로 만든다는 부분입니다.
원래는 슬랙에서 같이 얘기한대로 freeze()
를 호출하지 않았는데, 이랬을 때 Repository 계층에서 메인 스레드가 아닌 다른 백그라운드 스레드에서 생성된 Realm의 Entity 객체에 접근할 때 스레드 충돌로 인한 런타임 에러가 발생했습니다..
그래서 공식문서에서 권고한대로 frozen object의 형태로 만들어 리턴하도록 했고, Repository로 Entity를 넘겼을 때 충돌이 일어나지 않는 것을 확인했습니다.
공식문서 설명
https://www.mongodb.com/docs/realm/sdk/swift/crud/threading/#frozen-objects
Frozen Objects
Live, thread-confined objects work fine in most cases. However, some apps -- those based on reactive, event stream-based architectures, for example -- need to send immutable copies around to many threads for processing before ultimately ending up on the UI thread. Making a deep copy every time would be expensive, and Realm Database does not allow live instances to be shared across threads. In this case, you can freeze and thaw objects, collections, and realms.
Freezing creates an immutable view of a specific object, collection, or realm. The frozen object, collection, or realm still exists on disk, and does not need to be deeply copied when passed around to other threads. You can freely share the frozen object across threads without concern for thread issues. When you freeze a realm, its child objects also become frozen.
Frozen objects are not live and do not automatically update. They are effectively snapshots of the object state at the time of freezing. Thawing an object returns a live version of the frozen object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음 보는 신기한 개념이네요..
백그라운드 스레드에서 접근 가능한 Realm Entity 객체는 frozen 상태로 만들어서 Read-Only 이면서 깊은 복사가 아닌 얕은 복사로 만들어진 복사본으로 만들어야한다. 이런 말인거 같네요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네네 ㅋㅋ 사실 스레드 간에 데이터 전달할 때는 뭐든 freeze()
만 호출해주면 뭘하던 아무문제 없더라구요 ㅋㅋ
얼린다는게... 성능상 어떤지는 모르겠지만 일단 새로운 개념 하나 알았다 치고 넘어갔습니다 ㅋㅋㅋㅋ
|
||
self.semaphore.signal() | ||
} | ||
|
||
semaphore.wait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
원래는 semaphore을 사용하지 않고 진행하려 했는데 ㅠㅠ serial queue로 Realm 생성 및 데이터 읽기 작업을 할당하다보니, 해당 작업이 끝날 때 까지 메소드가 리턴되지 않도록 대기해야 되서 우선은 부득이하게 사용했습니다.
다만... 해당 코드는 아직도 좋다고 생각되지 않는게 semaphore 혹은 lock의 잘못된 사용은 자칫 교착상태 혹은 병목을 유발할 수 있기 때문에 이런거 없이 가는게 궁극적으로 맞는 것 같아요..
또한 공식문서에서도 읽기작업을 할 때는 lock을 사용하지 말라고 권고하고 있네요 ....
공식문서 설명
Don't lock to read:
Realm Database's Multiversion Concurrency Control (MVCC) architecture eliminates the need to lock for read operations. The values you read will never be corrupted or in a partially-modified state. You can freely read from realms on any thread without the need for locks or mutexes. Unnecessary locking would be a performance bottleneck since each thread might need to wait its turn before reading.
MVCC
는 보통 데이터베이스 쪽에서 많이 사용되는 아키텍쳐 용어 같던데... 여기까지 이해하자니 저도 야크털 깎는게 아닌가 싶네요 ㅋㅋㅋㅋ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
쉽지 않네요.. 저는 처음에 Thread-safe하게 만드신다고 하셔서, 읽기에 대한 작업은 동시 액세스 허용/ 쓰기에 대한 작업은 배타적 액세스 형태의 Thread-Safe인줄 알았는데, Realm의 특성상 모두 똑같은 스레드에서 접근해야하는 문제가 있어 읽기/쓰기 모두 배타적 액세스를 해줘야하는거 같아서 더 복잡해지는거 같아요..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵넵ㅋㅋ Realm 자체가 뭔가 바뀔 때마다 여러 버전의 스냅샷을 트리구조로 저장해놓고, 최신 버전을 사용한다.. 뭐 이런거 같은데
그래서 하나의 Realm을 생성하면, 이후에는 무조건 생성 작업을 처리한 스레드에서만 접근해야 되고 그래서 굳이 일관성 유지를 위해 lock을 할 필요가 없다고 이해했어요! (써놓고도 제대로 이해가 안가네요 ㅎㅎ)
하나 더 흥미로운건... 여러 스냅샷을 저장
하게 되면 파일 크기가 커지는거 아닌가? 하는 생각이 들었는데
Realm 사용과 파일 크기 와 같은 것도 공식문서에 있긴 하네요 ㅋㅋ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생 많으셨습니다!
필요한 부분에 다 코멘트를 추가해주셔서, 추가적으로 말씀드릴 내용이 없어 전체적인 코멘트만 작성해요!ㅎㅎ
"Realm에서는 lock을 권장하지 않는다" 라는 부분이 이번에 제일 헤메셨던 부분인거 같은데, 이 부분을 lock을 사용하지 않고 어떻게 배타적인 액세스를 허용할 수 있을지 한 번 같이 고민해봅시다!
RealmManager을 개선해봤는데 요약하면 크게 2가지입니다.
freeze()
를 한 번 호출시켜서 Realm 객체를 frozen object로 만들으서 데이터를 읽도록 했습니다.freeze()
를 통해 frozen object로 만들어서 넘겨야 한다고 하네요!