-
-
Notifications
You must be signed in to change notification settings - Fork 596
Split ObjectState into utilities and storage implementations #164
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
Conversation
Final code piece for #136 |
This change also brings ObjectState and the two State implementations to complete code coverage in unit tests. |
Just tried allocating 10MM objects with UniqueInstanceState while profiling node, watched it continually GC and recover memory :D |
w00t
|
cc @hallucinogen and @wangmengyan95 for review as well |
return Object.freeze( | ||
ObjectState.estimateAttributes(this.className, this._getStateIdentifier()) | ||
); | ||
let attributes = singleInstance ? |
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.
I think it would be better if we abstract this into InstanceStateController
that can be configured by CoreManager.js
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.
@hallucinogen I thought about that too, but they take completely different arguments -- one uses a set of strings as identifiers, the other uses an object. We could pass the Object and introspect the identifiers in most, but not all of the cases
So, we could probably abstract everything away and make this a controller, but one of the core reasons that ObjectStore exists, and why single-instance mode is addressable by a className + ID mapping, is to eventually publicize APIs that provide functional access over Parse Objects (a la Parse+React) without requiring any new code. Whether we consider the idea of SDK modularization or not, I like to think that it would be possible to build a reasonable lightweight application with just a Store, ParseOp, and RESTController. I also recognize the value of shared interfaces, so here's what I think we should do. Rather than make the first argument a EDIT: actually we can't. Somehow I logic'd that incorrectly on my walk to the bus -- I guess it's actually an example of why I don't necessarily want a constant interface. Need to think about this more. |
Continuing... another possible option is to replace the way we use In single instance mode, it returns an object of the shape This is my favorite approach thusfar |
This is going to be confusing, since the JS SDK is one level deeper than the other SDKs:
Does ParseReact publicize |
|
It's still possible to eventually publicize APIs that provide functional access over I agree with passing |
Maybe |
Object State is now controlled by ObjectStateController, and swappable like any other controller. This should be good for expandability. Also renamed |
export function getServerData(obj: ObjectIdentifier): AttributeMap { | ||
let state = getState(obj); | ||
if (state) { | ||
return state.serverData; |
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.
Do we want to be consistent in the default return value for all these functions?
return state.serverData || {};
like we did in getState
?
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.
Can you explain further? Each function's "default state" has a pretty specific meaning that is leveraged in the code. In general, I don't think that's the bit that should be up for review now; those are the things that are remaining the same before & after this diff.
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.
Oh, for your specific example, it's an invariant that state.serverData
exists if state
exists, check out the typedef at https://github.com/ParsePlatform/Parse-SDK-JS/pull/164/files#diff-5c0499aa286ed5bb8b6f6ad90b87830cR26
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.
ah i missed that - looks like it's all covered then.
Really like the extensibility now with |
setPendingOp: (obj: ParseObject, attr: string, op: ?Op) => void; | ||
pushPendingState: (obj: ParseObject) => void; | ||
popPendingState: (obj: ParseObject) => OpsMap; | ||
mergeFirstPendingState: (obj: ParseObject) => void; |
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.
Hmm, you sure this is correct? mergeFirstPendingState
of ObjectStateController
accepts ParseObject
.....
} | ||
|
||
_handleSaveError() { | ||
var pending = this._getPendingOps(); | ||
ObjectState.mergeFirstPendingState(this.className, this._getStateIdentifier()); | ||
let stateController = CoreManager.getObjectStateController(); | ||
stateController.mergeFirstPendingState(this._getStateIdentifier()); |
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.
... meanwhile in practice, it accepts stateIdentifier
....
Loving it! One question though |
I'll let @hallucinogen give the green light on this one. |
@@ -34,10 +33,12 @@ import { | |||
import ParsePromise from './ParsePromise'; | |||
import ParseQuery from './ParseQuery'; | |||
import ParseRelation from './ParseRelation'; | |||
import * as SingleInstanceState from './SingleInstanceState'; |
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.
I hope you won't hate me for this. But this should be SingleInstanceStateController
. I feel strongly about this since SingleInstanceState
sounds like model, while SingleInstanceStateController
clearly... a controller.
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.
nah, makes sense, though with every extra word I worry about things getting too Java-y. I'll go find-replace.
AbstractInstanceStateControllerFactory, here we come!
Boomdone. Really missing phab macros right about now |
@@ -207,6 +218,107 @@ module.exports = { | |||
return config['ObjectController']; | |||
}, | |||
|
|||
setObjectStateController(controller: ObjectStateController) { | |||
if (typeof controller.getState !== 'function') { | |||
console.log(controller); |
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.
productioncleanup
LGTM. One last nits, but accepting anyway. |
tyvm |
Split ObjectState into utilities and storage implementations
ObjectState used to both store object data and the perform the functionality to mutate it.
Now, to facilitate different behavior between single-instance mode and unique-instance mode, ObjectState simply contains the mutation behavior, independent of how it's stored.
This PR introduces
SingleInstanceState
andUniqueInstanceState
, storage layers that are directly accessed by ParseObject, and use ObjectState as a utility.This change is chiefly to prevent the memory leaks reported in #111
Essentially, there are 4 pieces:
I'll be adding more tests, but things look good for now.