M1 Mac用pymongo连MongoDB Atlas遇MemoryError问题求助
M1 Mac使用pymongo连接MongoDB Atlas时触发MemoryError(ffi.callback()相关)
环境信息
- Python版本:3.9.13
- pymongo版本:3.11.4(安装路径:
/Users/johnstern/opt/anaconda3/lib/python3.9/site-packages)
测试代码
import pymongo # 连接MongoDB数据库 connection_string = "<实际连接字符串已移除>" client = pymongo.MongoClient(connection_string)
错误回溯信息
--------------------------------------------------------------------------- MemoryError Traceback (most recent call last) Cell In[1], line 5 3 # Connect to the MongoDB database 4 connection_string = "<connection_string>" ----> 5 client = pymongo.MongoClient(connection_string) File ~/opt/anaconda3/lib/python3.9/site-packages/pymongo/mongo_client.py:691, in MongoClient.__init__(self, host, port, document_class, tz_aware, connect, type_registry, **kwargs) 684 if 'socketkeepalive' in opts: 685 warnings.warn( 686 "The socketKeepAlive option is deprecated. It now" 687 "defaults to true and disabling it is not recommended, see " 688 "https://docs.mongodb.com/manual/faq/diagnostics/" 689 "#does-tcp-keepalive-time-affect-mongodb-deployments", 690 DeprecationWarning, stacklevel=2) --> 691 self.__options = options = ClientOptions( 692 username, password, dbase, opts) 694 self.__default_database_name = dbase 695 self.__lock = threading.Lock() File ~/opt/anaconda3/lib/python3.9/site-packages/pymongo/client_options.py:161, in ClientOptions.__init__(self, username, password, database, options) 157 # self.__server_selection_timeout is in seconds. Must use full name for 158 # common.SERVER_SELECTION_TIMEOUT because it is set directly by tests. 159 self.__server_selection_timeout = options.get( 160 'serverselectiontimeoutms', common.SERVER_SELECTION_TIMEOUT) --> 161 self.__pool_options = _parse_pool_options(options) 162 self.__read_preference = _parse_read_preference(options) 163 self.__replica_set_name = options.get('replicaset') File ~/opt/anaconda3/lib/python3.9/site-packages/pymongo/client_options.py:131, in _parse_pool_options(options) 127 driver = options.get('driver') 128 compression_settings = CompressionSettings( 129 options.get('compressors', []), 130 options.get('zlibcompressionlevel', -1)) --> 131 ssl_context, ssl_match_hostname = _parse_ssl_options(options) 132 return PoolOptions(max_pool_size, 133 min_pool_size, 134 max_idle_time_seconds, (...) 140 driver, 141 compression_settings) File ~/opt/anaconda3/lib/python3.9/site-packages/pymongo/client_options.py:98, in _parse_ssl_options(options) 95 use_ssl = True 97 if use_ssl is True: --> 98 ctx = get_ssl_context( 99 certfile, 100 keyfile, 101 passphrase, 102 ca_certs, 103 cert_reqs, 104 crlfile, 105 match_hostname, 106 check_ocsp_endpoint) 107 return ctx, match_hostname 108 return None, match_hostname File ~/opt/anaconda3/lib/python3.9/site-packages/pymongo/ssl_support.py:108, in get_ssl_context(*args) 99 (certfile, 100 keyfile, 101 passphrase, (...) 105 match_hostname, 106 check_ocsp_endpoint) = args 107 verify_mode = CERT_REQUIRED if cert_reqs is None else cert_reqs --> 108 ctx = _ssl.SSLContext(_ssl.PROTOCOL_SSLv23) 109 # SSLContext.check_hostname was added in CPython 2.7.9 and 3.4. 110 if hasattr(ctx, "check_hostname"): File ~/opt/anaconda3/lib/python3.9/site-packages/pymongo/pyopenssl_context.py:184, in SSLContext.__init__(self, protocol) 179 # OCSP 180 # XXX: Find a better place to do this someday, since this is client 181 # side configuration and wrap_socket tries to support both client and 182 # server side sockets. 183 self._callback_data.check_ocsp_endpoint = True --> 184 self._ctx.set_ocsp_client_callback( 185 callback=_ocsp_callback, data=self._callback_data) File ~/opt/anaconda3/lib/python3.9/site-packages/OpenSSL/SSL.py:1580, in Context.set_ocsp_client_callback(self, callback, data) 1563 def set_ocsp_client_callback(self, callback, data=None): 1564 """ 1565 Set a callback to validate OCSP data stapled to the TLS handshake on 1566 the client side. (...) 1578 used. This parameter is optional. 1579 """ -> 1580 helper = _OCSPClientCallbackHelper(callback) 1581 self._set_ocsp_callback(helper, data) File ~/opt/anaconda3/lib/python3.9/site-packages/OpenSSL/SSL.py:557, in _OCSPClientCallbackHelper.__init__(self, callback) 554 # Return negative value if an exception is hit. 555 return -1 --> 557 self.callback = _ffi.callback("int (*)(SSL *, void *)", wrapper) MemoryError: Cannot allocate write+execute memory for ffi.callback(). You might be running on a system that prevents this.
错误原因分析
这个问题主要和M1 Mac的ARM架构、旧版本pymongo及依赖库的兼容性有关:
- ARM架构适配不足:M1采用ARM64架构,你使用的pymongo 3.11.4版本发布时ARM64桌面平台还未普及,对该架构的支持不完善。在处理SSL/TLS的OCSP回调时,cffi库无法在ARM64环境下正确分配可写可执行内存页——ARM平台的内存保护机制和x86存在差异,旧版本cffi未适配这种区别。
- 依赖库版本过旧:pymongo 3.11.4依赖的pyOpenSSL、cffi等库版本较低,这些旧版本在M1的ARM64环境下存在内存分配bug,导致无法创建ffi回调所需的内存区域。
- Anaconda环境的潜在问题:你使用的Anaconda环境版本可能较老,对M1的ARM支持不够成熟,可能导致依赖库未正确编译为ARM64版本,而是通过Rosetta模拟x86运行,进一步引发内存分配异常。
内容的提问来源于stack exchange,提问作者John Stern
相关产品推荐
相关产品推荐

